diff options
| author | Mitch Curtis <mitch.curtis@qt.io> | 2023-03-07 16:06:29 +0800 |
|---|---|---|
| committer | Mitch Curtis <mitch.curtis@qt.io> | 2023-03-08 14:50:54 +0800 |
| commit | 4f5b4f81b0e73f264a19a690b8795f776778fa3c (patch) | |
| tree | 040a67918b165ea7f604823dfebffe7e2f3f4d4e | |
| parent | ee3d11f70bf7d5d7f3b0f4f0b531a0ed2a20e721 (diff) | |
Material: fix placeholder text y position when control is too small
Ensure that it's sensibly positioned despite its size.
Fixes: QTBUG-111515
Pick-to: 6.5 6.5.0
Change-Id: I71816c461ff1d2f85e010bf871ab1b7ef2ccaf6e
Reviewed-by: Oliver Eftevaag <oliver.eftevaag@qt.io>
5 files changed, 91 insertions, 12 deletions
diff --git a/src/quickcontrols/material/TextArea.qml b/src/quickcontrols/material/TextArea.qml index 545cadee82..32f6d5d5f7 100644 --- a/src/quickcontrols/material/TextArea.qml +++ b/src/quickcontrols/material/TextArea.qml @@ -50,6 +50,7 @@ T.TextArea { controlHasActiveFocus: control.activeFocus controlHasText: control.length > 0 controlImplicitBackgroundHeight: control.implicitBackgroundHeight + controlHeight: control.height } background: MaterialTextContainer { diff --git a/src/quickcontrols/material/TextField.qml b/src/quickcontrols/material/TextField.qml index e2ec69dc27..b8ddd55cf4 100644 --- a/src/quickcontrols/material/TextField.qml +++ b/src/quickcontrols/material/TextField.qml @@ -50,6 +50,7 @@ T.TextField { controlHasActiveFocus: control.activeFocus controlHasText: control.length > 0 controlImplicitBackgroundHeight: control.implicitBackgroundHeight + controlHeight: control.height } background: MaterialTextContainer { diff --git a/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp index b908a62fc0..40557f46a2 100644 --- a/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp +++ b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp @@ -9,6 +9,7 @@ #include <QtGui/qpainterpath.h> #include <QtQml/qqmlinfo.h> #include <QtQuickTemplates2/private/qquicktheme_p.h> +#include <QtQuickTemplates2/private/qquicktextarea_p.h> QT_BEGIN_NAMESPACE @@ -100,13 +101,25 @@ bool QQuickMaterialPlaceholderText::shouldAnimate() const : !m_controlHasText && !text().isEmpty(); } +void QQuickMaterialPlaceholderText::updateY() +{ + setY(shouldFloat() ? floatingTargetY() : normalTargetY()); +} + qreal QQuickMaterialPlaceholderText::normalTargetY() const { + auto *textArea = qobject_cast<QQuickTextArea *>(parentItem()); + if (textArea && m_controlHeight >= textArea->implicitHeight()) { + // TextArea can be multiple lines in height, and we want the + // placeholder text to sit in the middle of its default-height + // (one-line) if its explicit height is greater than or equal to its + // implicit height - i.e. if it has room for it. If it doesn't have + // room, just do what TextField does. + return (m_controlImplicitBackgroundHeight - m_largestHeight) / 2.0; + } + // When the placeholder text shouldn't float, it should sit in the middle of the TextField. - // We could just use the control's height minus our height instead of the members, but - // that doesn't work for TextArea, which can be multiple lines in height and hence taller. - // In that case, we want the placeholder text to sit in the middle of its default-height (one-line). - return (m_controlImplicitBackgroundHeight - m_largestHeight) / 2.0; + return (m_controlHeight - height()) / 2.0; } qreal QQuickMaterialPlaceholderText::floatingTargetY() const @@ -142,10 +155,34 @@ void QQuickMaterialPlaceholderText::setControlImplicitBackgroundHeight(qreal con return; m_controlImplicitBackgroundHeight = controlImplicitBackgroundHeight; - setY(shouldFloat() ? floatingTargetY() : normalTargetY()); + updateY(); emit controlImplicitBackgroundHeightChanged(); } +/*! + \internal + + Exists so that we can call updateY when the control's height changes, + which is necessary for some y position calculations. + + We don't really need it for the actual calculations, since we already + have access to the parent item, from which the property comes, but + it's simpler just to use it. +*/ +qreal QQuickMaterialPlaceholderText::controlHeight() const +{ + return m_controlHeight; +} + +void QQuickMaterialPlaceholderText::setControlHeight(qreal controlHeight) +{ + if (qFuzzyCompare(m_controlHeight, controlHeight)) + return; + + m_controlHeight = controlHeight; + updateY(); +} + qreal QQuickMaterialPlaceholderText::verticalPadding() const { return m_verticalPadding; @@ -185,8 +222,7 @@ void QQuickMaterialPlaceholderText::controlGotActiveFocus() m_focusInAnimation->start(QAbstractAnimation::DeleteWhenStopped); } else { - const int newY = shouldFloat() ? floatingTargetY() : normalTargetY(); - setY(newY); + updateY(); } } @@ -212,16 +248,14 @@ void QQuickMaterialPlaceholderText::controlLostActiveFocus() m_focusOutAnimation->start(QAbstractAnimation::DeleteWhenStopped); } else { - const int newY = shouldFloat() ? floatingTargetY() : normalTargetY(); - setY(newY); + updateY(); } } void QQuickMaterialPlaceholderText::maybeSetFocusAnimationProgress() { - const bool shouldWeFloat = shouldFloat(); - setY(shouldWeFloat ? floatingTargetY() : normalTargetY()); - setScale(shouldWeFloat ? floatingScale : 1.0); + updateY(); + setScale(shouldFloat() ? floatingScale : 1.0); } void QQuickMaterialPlaceholderText::componentComplete() diff --git a/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h index 525e079c28..22e8b4e99a 100644 --- a/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h +++ b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h @@ -34,6 +34,7 @@ class QQuickMaterialPlaceholderText : public QQuickPlaceholderText Q_PROPERTY(qreal verticalPadding READ verticalPadding WRITE setVerticalPadding NOTIFY verticalPaddingChanged FINAL) Q_PROPERTY(qreal controlImplicitBackgroundHeight READ controlImplicitBackgroundHeight WRITE setControlImplicitBackgroundHeight NOTIFY controlImplicitBackgroundHeightChanged FINAL) + Q_PROPERTY(qreal controlHeight READ controlHeight WRITE setControlHeight FINAL) QML_NAMED_ELEMENT(FloatingPlaceholderText) QML_ADDED_IN_VERSION(6, 5) @@ -54,6 +55,9 @@ public: qreal controlImplicitBackgroundHeight() const; void setControlImplicitBackgroundHeight(qreal controlImplicitBackgroundHeight); + qreal controlHeight() const; + void setControlHeight(qreal controlHeight); + qreal verticalPadding() const; void setVerticalPadding(qreal verticalPadding); @@ -69,6 +73,7 @@ private: bool shouldFloat() const; bool shouldAnimate() const; + void updateY(); qreal normalTargetY() const; qreal floatingTargetY() const; @@ -85,6 +90,7 @@ private: int m_largestHeight = 0; qreal m_verticalPadding = 0; qreal m_controlImplicitBackgroundHeight = 0; + qreal m_controlHeight = 0; QPointer<QParallelAnimationGroup> m_focusInAnimation; QPointer<QParallelAnimationGroup> m_focusOutAnimation; }; diff --git a/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml b/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml index f627f7ee1b..46dd5b42d0 100644 --- a/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml +++ b/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml @@ -7,6 +7,7 @@ import QtTest import QtQuick.Templates as T import QtQuick.Controls import QtQuick.Controls.Material +import QtQuick.Controls.Material.impl as MaterialImpl TestCase { id: testCase @@ -953,4 +954,40 @@ TestCase { verify(item) compare(item["__isDiscrete"], data.expectTickmarks) } + + Component { + id: textFieldComponent + TextField {} + } + + Component { + id: textAreaComponent + TextArea {} + } + + function test_placeholderText() { + { + // The non-floating placeholder text should be in the middle of TextField regardless of its height. + let textField = createTemporaryObject(textFieldComponent, testCase, { placeholderText: "TextField" }) + verify(textField) + let placeholderTextItem = textField.children[0] + verify(placeholderTextItem as MaterialImpl.FloatingPlaceholderText) + compare(placeholderTextItem.y, (textField.height - placeholderTextItem.height) / 2) + textField.height = 10 + compare(placeholderTextItem.y, (textField.height - placeholderTextItem.height) / 2) + textField.destroy() + } + + { + // The non-floating placeholder text should be near the top of TextArea while it has room, but when it + // doesn't have room, it should start behaving like TextField's. + let textArea = createTemporaryObject(textAreaComponent, testCase, { placeholderText: "TextArea" }) + verify(textArea) + let placeholderTextItem = textArea.children[0] + verify(placeholderTextItem as MaterialImpl.FloatingPlaceholderText) + compare(placeholderTextItem.y, (placeholderTextItem.controlImplicitBackgroundHeight - placeholderTextItem.largestHeight) / 2) + textArea.height = 10 + compare(placeholderTextItem.y, (textArea.height - placeholderTextItem.height) / 2) + } + } } |
