diff --git a/src/quickcontrols/material/TextArea.qml b/src/quickcontrols/material/TextArea.qml index f4b0b73345..e071fb8860 100644 --- a/src/quickcontrols/material/TextArea.qml +++ b/src/quickcontrols/material/TextArea.qml @@ -73,6 +73,7 @@ T.TextArea { // When the control's size is set larger than its implicit size, use whatever size is smaller // so that the gap isn't too big. placeholderTextWidth: Math.min(placeholder.width, placeholder.implicitWidth) * placeholder.scale + placeholderTextHAlign: control.effectiveHorizontalAlignment controlHasActiveFocus: control.activeFocus controlHasText: control.length > 0 placeholderHasText: placeholder.text.length > 0 diff --git a/src/quickcontrols/material/TextField.qml b/src/quickcontrols/material/TextField.qml index d29b247ce1..7186ac9d2b 100644 --- a/src/quickcontrols/material/TextField.qml +++ b/src/quickcontrols/material/TextField.qml @@ -69,6 +69,7 @@ T.TextField { // When the control's size is set larger than its implicit size, use whatever size is smaller // so that the gap isn't too big. placeholderTextWidth: Math.min(placeholder.width, placeholder.implicitWidth) * placeholder.scale + placeholderTextHAlign: control.effectiveHorizontalAlignment controlHasActiveFocus: control.activeFocus controlHasText: control.length > 0 placeholderHasText: placeholder.text.length > 0 diff --git a/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp index 82eac0a27b..788ab71125 100644 --- a/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp +++ b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp @@ -29,8 +29,8 @@ Q_GLOBAL_STATIC(QEasingCurve, animationEasingCurve, QEasingCurve::OutSine); QQuickMaterialPlaceholderText::QQuickMaterialPlaceholderText(QQuickItem *parent) : QQuickPlaceholderText(parent) { - // Ensure that scaling happens on the left side, at the vertical center. - setTransformOrigin(QQuickItem::Left); + connect(this, &QQuickMaterialPlaceholderText::effectiveHorizontalAlignmentChanged, + this, &QQuickMaterialPlaceholderText::adjustTransformOrigin); } bool QQuickMaterialPlaceholderText::isFilled() const @@ -212,6 +212,23 @@ void QQuickMaterialPlaceholderText::setVerticalPadding(qreal verticalPadding) emit verticalPaddingChanged(); } +void QQuickMaterialPlaceholderText::adjustTransformOrigin() +{ + switch (effectiveHAlign()) { + case QQuickText::AlignLeft: + Q_FALLTHROUGH(); + case QQuickText::AlignJustify: + setTransformOrigin(QQuickItem::Left); + break; + case QQuickText::AlignRight: + setTransformOrigin(QQuickItem::Right); + break; + case QQuickText::AlignHCenter: + setTransformOrigin(QQuickItem::Center); + break; + } +} + void QQuickMaterialPlaceholderText::controlGotActiveFocus() { if (m_focusOutAnimation) @@ -275,9 +292,9 @@ void QQuickMaterialPlaceholderText::maybeSetFocusAnimationProgress() void QQuickMaterialPlaceholderText::componentComplete() { - // We deliberately do not call QQuickPlaceholderText's implementation here, - // as Material 3 placeholder text should always be left-aligned. - QQuickText::componentComplete(); + QQuickPlaceholderText::componentComplete(); + + adjustTransformOrigin(); m_largestHeight = implicitHeight(); if (m_largestHeight > 0) { diff --git a/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h index 7771adf08a..bf851d367a 100644 --- a/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h +++ b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h @@ -71,6 +71,9 @@ signals: void controlImplicitBackgroundHeightChanged(); void verticalPaddingChanged(); +private slots: + void adjustTransformOrigin(); + private: bool shouldFloat() const; bool shouldAnimate() const; diff --git a/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp b/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp index e987ce5610..ab07c9d9b2 100644 --- a/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp +++ b/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp @@ -118,6 +118,20 @@ void QQuickMaterialTextContainer::setPlaceholderTextWidth(qreal placeholderTextW update(); } +QQuickMaterialTextContainer::PlaceHolderHAlignment QQuickMaterialTextContainer::placeholderTextHAlign() const +{ + return m_placeholderTextHAlign; +} + +void QQuickMaterialTextContainer::setPlaceholderTextHAlign(PlaceHolderHAlignment placeholderTextHAlign) +{ + if (m_placeholderTextHAlign == placeholderTextHAlign) + return; + + m_placeholderTextHAlign = placeholderTextHAlign; + update(); +} + bool QQuickMaterialTextContainer::controlHasActiveFocus() const { return m_controlHasActiveFocus; @@ -208,6 +222,25 @@ void QQuickMaterialTextContainer::paint(QPainter *painter) // This is coincidentally the same as cornerRadius, but use different variable names // to keep the code understandable. const qreal gapPadding = 4; + // When animating focus on outlined containers, we need to make a gap + // at the top left for the placeholder text. + // If the text is too wide for the container, it will be elided, so + // we shouldn't need to clamp its width here. TODO: check that this is the case for TextArea. + const qreal halfPlaceholderWidth = m_placeholderTextWidth / 2; + // Take care of different Alignment cases for the placeholder text. + qreal gapCenterX; + switch (m_placeholderTextHAlign) { + case PlaceHolderHAlignment::AlignHCenter: + gapCenterX = width() / 2; + break; + case PlaceHolderHAlignment::AlignRight: + gapCenterX = width() - halfPlaceholderWidth - m_horizontalPadding; + break; + default: + gapCenterX = m_horizontalPadding + halfPlaceholderWidth; + break; + } + QPainterPath path; QPointF startPos; @@ -216,15 +249,6 @@ void QQuickMaterialTextContainer::paint(QPainter *painter) if (m_filled || m_focusAnimationProgress == 0) { startPos = QPointF(cornerRadius, 0); } else { - // When animating focus on outlined containers, we need to make a gap - // at the top left for the placeholder text. - // If the text is too wide for the container, it will be elided, so - // we shouldn't need to clamp its width here. TODO: check that this is the case for TextArea. - const qreal halfPlaceholderWidth = m_placeholderTextWidth / 2; - // Left padding plus half of the placeholder text gives the center of the placeholder text gap. - // Note that the placeholder gap is always aligned to the left side of the TextField, - // not the center, so we can't just use half the container's width. - const qreal gapCenterX = m_horizontalPadding + halfPlaceholderWidth; // Start at the center of the gap and animate outwards towards the left-hand side. // Subtract gapPadding to account for the gap between the line and the placeholder text. // Also subtract the pen width because otherwise it extends by that distance too much to the right. @@ -259,11 +283,6 @@ void QQuickMaterialTextContainer::paint(QPainter *painter) // Back to the start. path.lineTo(startPos.x(), startPos.y()); } else { - // Go to the end (right-hand side) of the gap. - const qreal halfPlaceholderWidth = (/*placeholderTextGap * 2 + */m_placeholderTextWidth) / 2; - const qreal gapCenterX = m_horizontalPadding + halfPlaceholderWidth; - // Just "+ placeholderTextGap" should be enough to get us to the correct place - not - // sure why doubling it is necessary... path.lineTo(gapCenterX + (m_focusAnimationProgress * halfPlaceholderWidth) + gapPadding, startPos.y()); } diff --git a/src/quickcontrols/material/impl/qquickmaterialtextcontainer_p.h b/src/quickcontrols/material/impl/qquickmaterialtextcontainer_p.h index ed6876cb46..648e83521f 100644 --- a/src/quickcontrols/material/impl/qquickmaterialtextcontainer_p.h +++ b/src/quickcontrols/material/impl/qquickmaterialtextcontainer_p.h @@ -32,6 +32,7 @@ class QQuickMaterialTextContainer : public QQuickPaintedItem Q_PROPERTY(QColor focusedOutlineColor READ focusedOutlineColor WRITE setFocusedOutlineColor FINAL) Q_PROPERTY(qreal focusAnimationProgress READ focusAnimationProgress WRITE setFocusAnimationProgress FINAL) Q_PROPERTY(qreal placeholderTextWidth READ placeholderTextWidth WRITE setPlaceholderTextWidth FINAL) + Q_PROPERTY(PlaceHolderHAlignment placeholderTextHAlign READ placeholderTextHAlign WRITE setPlaceholderTextHAlign FINAL) Q_PROPERTY(bool controlHasText READ controlHasText WRITE setControlHasText NOTIFY controlHasTextChanged FINAL) Q_PROPERTY(bool placeholderHasText READ placeholderHasText WRITE setPlaceholderHasText NOTIFY placeholderHasTextChanged FINAL) Q_PROPERTY(int horizontalPadding READ horizontalPadding WRITE setHorizontalPadding NOTIFY horizontalPaddingChanged FINAL) @@ -41,6 +42,14 @@ class QQuickMaterialTextContainer : public QQuickPaintedItem public: explicit QQuickMaterialTextContainer(QQuickItem *parent = nullptr); + enum PlaceHolderHAlignment { + AlignLeft = Qt::AlignLeft, + AlignRight = Qt::AlignRight, + AlignHCenter = Qt::AlignHCenter, + AlignJustify = Qt::AlignJustify + }; + Q_ENUM(PlaceHolderHAlignment) + bool isFilled() const; void setFilled(bool filled); @@ -73,6 +82,9 @@ public: void paint(QPainter *painter) override; + PlaceHolderHAlignment placeholderTextHAlign() const; + void setPlaceholderTextHAlign(PlaceHolderHAlignment placeHolderTextHAlign); + signals: void animateChanged(); void controlHasActiveFocusChanged(); @@ -102,6 +114,7 @@ private: bool m_controlHasText = false; bool m_placeholderHasText = false; int m_horizontalPadding = 0; + PlaceHolderHAlignment m_placeholderTextHAlign; }; QT_END_NAMESPACE diff --git a/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml b/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml index a5ae0201c2..ae42355dfd 100644 --- a/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml +++ b/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml @@ -984,10 +984,10 @@ TestCase { verify(textField) let placeholderTextItem = textField.children[0] verify(placeholderTextItem as MaterialImpl.FloatingPlaceholderText) - compare(placeholderTextItem.horizontalAlignment, TextField.AlignLeft) + compare(placeholderTextItem.horizontalAlignment, data.horizontalAlignment) textField.forceActiveFocus() - compare(placeholderTextItem.horizontalAlignment, TextField.AlignLeft) + compare(placeholderTextItem.horizontalAlignment, data.horizontalAlignment) textField.destroy() } @@ -995,7 +995,8 @@ TestCase { return [ { tag: "AlignLeft", horizontalAlignment: TextArea.AlignLeft }, { tag: "AlignHCenter", horizontalAlignment: TextArea.AlignHCenter }, - { tag: "AlignRight", horizontalAlignment: TextArea.AlignRight } + { tag: "AlignRight", horizontalAlignment: TextArea.AlignRight }, + { tag: "AlignJustify", horizontalAlignment: TextArea.AlignJustify } ] } @@ -1008,10 +1009,10 @@ TestCase { verify(textArea) let placeholderTextItem = textArea.children[0] verify(placeholderTextItem as MaterialImpl.FloatingPlaceholderText) - compare(placeholderTextItem.horizontalAlignment, TextArea.AlignLeft) + compare(placeholderTextItem.horizontalAlignment, data.horizontalAlignment) textArea.forceActiveFocus() - compare(placeholderTextItem.horizontalAlignment, TextArea.AlignLeft) + compare(placeholderTextItem.horizontalAlignment, data.horizontalAlignment) } function test_placeholderTextPos() {