aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMitch Curtis <mitch.curtis@qt.io>2022-12-28 14:36:32 +0800
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-02-16 22:26:44 +0000
commita62dd478564f2f5e1fe9bd006551239c7b820073 (patch)
treec80acdb63fcb115bf738a6fc09cbf56338bd98aa
parent19bafadc4840c7ed1f77e632821fcb732c0b93c9 (diff)
Update Material TextField to Material 3
Fixes: QTBUG-72554 Fixes: QTBUG-109218 Change-Id: I0bc6fc3d16630352dcd5c58c5dd2b1bf794741c5 Reviewed-by: Oliver Eftevaag <oliver.eftevaag@qt.io> (cherry picked from commit 20e3d1b522d1b79239e9ac4a6af47ce3648512bd) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/quickcontrols/material/TextField.qml47
-rw-r--r--src/quickcontrols/material/impl/CMakeLists.txt4
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp245
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h94
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp385
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialtextcontainer_p.h108
-rw-r--r--src/quickcontrols/material/qquickmaterialstyle.cpp22
-rw-r--r--src/quickcontrols/material/qquickmaterialstyle_p.h8
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_textfield.qml18
-rw-r--r--tests/auto/quickcontrols/qquicktextfield/tst_qquicktextfield.cpp6
-rw-r--r--tests/manual/quickcontrols/material/CMakeLists.txt1
-rw-r--r--tests/manual/quickcontrols/material/material.qml2
-rw-r--r--tests/manual/quickcontrols/material/pages/TextFieldPage.qml86
13 files changed, 1002 insertions, 24 deletions
diff --git a/src/quickcontrols/material/TextField.qml b/src/quickcontrols/material/TextField.qml
index 598f66f938..e2ec69dc27 100644
--- a/src/quickcontrols/material/TextField.qml
+++ b/src/quickcontrols/material/TextField.qml
@@ -13,11 +13,17 @@ T.TextField {
implicitWidth: implicitBackgroundWidth + leftInset + rightInset
|| Math.max(contentWidth, placeholder.implicitWidth) + leftPadding + rightPadding
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
- contentHeight + topPadding + bottomPadding,
- placeholder.implicitHeight + topPadding + bottomPadding)
+ contentHeight + topPadding + bottomPadding)
- topPadding: 8
- bottomPadding: 16
+ leftPadding: Material.textFieldHorizontalPadding
+ rightPadding: Material.textFieldHorizontalPadding
+ // Need to account for the placeholder text when it's sitting on top.
+ topPadding: Material.containerStyle === Material.Filled
+ ? placeholderText.length > 0 && (activeFocus || length > 0)
+ ? Material.textFieldVerticalPadding + placeholder.largestHeight
+ : Material.textFieldVerticalPadding
+ : Material.textFieldVerticalPadding
+ bottomPadding: Material.textFieldVerticalPadding
color: enabled ? Material.foreground : Material.hintTextColor
selectionColor: Material.accentColor
@@ -25,28 +31,41 @@ T.TextField {
placeholderTextColor: Material.hintTextColor
verticalAlignment: TextInput.AlignVCenter
+ Material.containerStyle: Material.Outlined
+
cursorDelegate: CursorDelegate { }
- PlaceholderText {
+ FloatingPlaceholderText {
id: placeholder
x: control.leftPadding
- y: control.topPadding
width: control.width - (control.leftPadding + control.rightPadding)
- height: control.height - (control.topPadding + control.bottomPadding)
text: control.placeholderText
font: control.font
color: control.placeholderTextColor
- verticalAlignment: control.verticalAlignment
elide: Text.ElideRight
renderType: control.renderType
- visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
+
+ filled: control.Material.containerStyle === Material.Filled
+ verticalPadding: control.Material.textFieldVerticalPadding
+ controlHasActiveFocus: control.activeFocus
+ controlHasText: control.length > 0
+ controlImplicitBackgroundHeight: control.implicitBackgroundHeight
}
- background: Rectangle {
- y: control.height - height - control.bottomPadding + 8
+ background: MaterialTextContainer {
implicitWidth: 120
- height: control.activeFocus || (enabled && control.hovered) ? 2 : 1
- color: control.activeFocus ? control.Material.accentColor
- : ((enabled && control.hovered) ? control.Material.primaryTextColor : control.Material.hintTextColor)
+ implicitHeight: control.Material.textFieldHeight
+
+ filled: control.Material.containerStyle === Material.Filled
+ fillColor: control.Material.textFieldFilledContainerColor
+ outlineColor: (enabled && control.hovered) ? control.Material.primaryTextColor : control.Material.hintTextColor
+ focusedOutlineColor: control.Material.accentColor
+ // 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
+ controlHasActiveFocus: control.activeFocus
+ controlHasText: control.length > 0
+ placeholderHasText: placeholder.text.length > 0
+ horizontalPadding: control.Material.textFieldHorizontalPadding
}
}
diff --git a/src/quickcontrols/material/impl/CMakeLists.txt b/src/quickcontrols/material/impl/CMakeLists.txt
index ebf3e1cbf3..abae353f44 100644
--- a/src/quickcontrols/material/impl/CMakeLists.txt
+++ b/src/quickcontrols/material/impl/CMakeLists.txt
@@ -28,8 +28,10 @@ qt_internal_add_qml_module(qtquickcontrols2materialstyleimplplugin
NO_PLUGIN_OPTIONAL
SOURCES
qquickmaterialbusyindicator.cpp qquickmaterialbusyindicator_p.h
+ qquickmaterialplaceholdertext.cpp qquickmaterialplaceholdertext_p.h
qquickmaterialprogressbar.cpp qquickmaterialprogressbar_p.h
qquickmaterialripple.cpp qquickmaterialripple_p.h
+ qquickmaterialtextcontainer.cpp qquickmaterialtextcontainer_p.h
QML_FILES
${qml_files}
DEFINES
@@ -38,8 +40,10 @@ qt_internal_add_qml_module(qtquickcontrols2materialstyleimplplugin
LIBRARIES
Qt::CorePrivate
Qt::Gui
+ Qt::Qml
Qt::QmlPrivate
Qt::QuickControls2ImplPrivate
+ Qt::Quick
Qt::QuickPrivate
Qt::QuickTemplates2Private
)
diff --git a/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp
new file mode 100644
index 0000000000..b908a62fc0
--- /dev/null
+++ b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp
@@ -0,0 +1,245 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickmaterialplaceholdertext_p.h"
+
+#include <QtCore/qpropertyanimation.h>
+#include <QtCore/qparallelanimationgroup.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qpainterpath.h>
+#include <QtQml/qqmlinfo.h>
+#include <QtQuickTemplates2/private/qquicktheme_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static const qreal floatingScale = 0.8;
+Q_GLOBAL_STATIC(QEasingCurve, animationEasingCurve, QEasingCurve::OutSine);
+
+/*
+ This class makes it easier to animate the various placeholder text changes
+ for each type of text container (filled, outlined).
+
+ By doing animations in C++, we avoid having a bunch of states, transitions,
+ and animations (which are all QObjects) declared in QML, even if that text
+ control never gets focus and hence never needs them.
+*/
+
+QQuickMaterialPlaceholderText::QQuickMaterialPlaceholderText(QQuickItem *parent)
+ : QQuickPlaceholderText(parent)
+{
+ // Ensure that scaling happens on the left side, at the vertical center.
+ setTransformOrigin(QQuickItem::Left);
+}
+
+bool QQuickMaterialPlaceholderText::isFilled() const
+{
+ return m_filled;
+}
+
+void QQuickMaterialPlaceholderText::setFilled(bool filled)
+{
+ if (filled == m_filled)
+ return;
+
+ m_filled = filled;
+ update();
+ void filledChanged();
+}
+
+bool QQuickMaterialPlaceholderText::controlHasActiveFocus() const
+{
+ return m_controlHasActiveFocus;
+}
+
+void QQuickMaterialPlaceholderText::setControlHasActiveFocus(bool controlHasActiveFocus)
+{
+ if (m_controlHasActiveFocus == controlHasActiveFocus)
+ return;
+
+ m_controlHasActiveFocus = controlHasActiveFocus;
+ if (m_controlHasActiveFocus)
+ controlGotActiveFocus();
+ else
+ controlLostActiveFocus();
+ emit controlHasActiveFocusChanged();
+}
+
+bool QQuickMaterialPlaceholderText::controlHasText() const
+{
+ return m_controlHasText;
+}
+
+void QQuickMaterialPlaceholderText::setControlHasText(bool controlHasText)
+{
+ if (m_controlHasText == controlHasText)
+ return;
+
+ m_controlHasText = controlHasText;
+ maybeSetFocusAnimationProgress();
+ emit controlHasTextChanged();
+}
+
+/*
+ Placeholder text of outlined text fields should float when:
+ - There is placeholder text, and
+ - The control has active focus, or
+ - The control has text
+*/
+bool QQuickMaterialPlaceholderText::shouldFloat() const
+{
+ const bool controlHasActiveFocusOrText = m_controlHasActiveFocus || m_controlHasText;
+ return m_filled
+ ? controlHasActiveFocusOrText
+ : !text().isEmpty() && controlHasActiveFocusOrText;
+}
+
+bool QQuickMaterialPlaceholderText::shouldAnimate() const
+{
+ return m_filled
+ ? !m_controlHasText
+ : !m_controlHasText && !text().isEmpty();
+}
+
+qreal QQuickMaterialPlaceholderText::normalTargetY() const
+{
+ // 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;
+}
+
+qreal QQuickMaterialPlaceholderText::floatingTargetY() const
+{
+ // For filled text fields, the placeholder text sits just above
+ // the text when floating.
+ if (m_filled)
+ return m_verticalPadding;
+
+ // Outlined text fields have the placeaholder vertically centered
+ // along the outline at the top.
+ return -m_largestHeight / 2;
+}
+
+/*!
+ \internal
+
+ The height of the text at its largest size that we set.
+*/
+int QQuickMaterialPlaceholderText::largestHeight() const
+{
+ return m_largestHeight;
+}
+
+qreal QQuickMaterialPlaceholderText::controlImplicitBackgroundHeight() const
+{
+ return m_controlImplicitBackgroundHeight;
+}
+
+void QQuickMaterialPlaceholderText::setControlImplicitBackgroundHeight(qreal controlImplicitBackgroundHeight)
+{
+ if (qFuzzyCompare(m_controlImplicitBackgroundHeight, controlImplicitBackgroundHeight))
+ return;
+
+ m_controlImplicitBackgroundHeight = controlImplicitBackgroundHeight;
+ setY(shouldFloat() ? floatingTargetY() : normalTargetY());
+ emit controlImplicitBackgroundHeightChanged();
+}
+
+qreal QQuickMaterialPlaceholderText::verticalPadding() const
+{
+ return m_verticalPadding;
+}
+
+void QQuickMaterialPlaceholderText::setVerticalPadding(qreal verticalPadding)
+{
+ if (qFuzzyCompare(m_verticalPadding, verticalPadding))
+ return;
+
+ m_verticalPadding = verticalPadding;
+ emit verticalPaddingChanged();
+}
+
+void QQuickMaterialPlaceholderText::controlGotActiveFocus()
+{
+ if (m_focusOutAnimation)
+ m_focusOutAnimation->stop();
+
+ Q_ASSERT(!m_focusInAnimation);
+ if (shouldAnimate()) {
+ m_focusInAnimation = new QParallelAnimationGroup(this);
+
+ QPropertyAnimation *yAnimation = new QPropertyAnimation(this, "y", this);
+ yAnimation->setDuration(300);
+ yAnimation->setStartValue(y());
+ yAnimation->setEndValue(floatingTargetY());
+ yAnimation->setEasingCurve(*animationEasingCurve);
+ m_focusInAnimation->addAnimation(yAnimation);
+
+ auto *scaleAnimation = new QPropertyAnimation(this, "scale", this);
+ scaleAnimation->setDuration(300);
+ scaleAnimation->setStartValue(1);
+ scaleAnimation->setEndValue(floatingScale);
+ yAnimation->setEasingCurve(*animationEasingCurve);
+ m_focusInAnimation->addAnimation(scaleAnimation);
+
+ m_focusInAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+ } else {
+ const int newY = shouldFloat() ? floatingTargetY() : normalTargetY();
+ setY(newY);
+ }
+}
+
+void QQuickMaterialPlaceholderText::controlLostActiveFocus()
+{
+ Q_ASSERT(!m_focusOutAnimation);
+ if (shouldAnimate()) {
+ m_focusOutAnimation = new QParallelAnimationGroup(this);
+
+ auto *yAnimation = new QPropertyAnimation(this, "y", this);
+ yAnimation->setDuration(300);
+ yAnimation->setStartValue(y());
+ yAnimation->setEndValue(normalTargetY());
+ yAnimation->setEasingCurve(*animationEasingCurve);
+ m_focusOutAnimation->addAnimation(yAnimation);
+
+ auto *scaleAnimation = new QPropertyAnimation(this, "scale", this);
+ scaleAnimation->setDuration(300);
+ scaleAnimation->setStartValue(floatingScale);
+ scaleAnimation->setEndValue(1);
+ yAnimation->setEasingCurve(*animationEasingCurve);
+ m_focusOutAnimation->addAnimation(scaleAnimation);
+
+ m_focusOutAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+ } else {
+ const int newY = shouldFloat() ? floatingTargetY() : normalTargetY();
+ setY(newY);
+ }
+}
+
+void QQuickMaterialPlaceholderText::maybeSetFocusAnimationProgress()
+{
+ const bool shouldWeFloat = shouldFloat();
+ setY(shouldWeFloat ? floatingTargetY() : normalTargetY());
+ setScale(shouldWeFloat ? floatingScale : 1.0);
+}
+
+void QQuickMaterialPlaceholderText::componentComplete()
+{
+ QQuickPlaceholderText::componentComplete();
+
+ if (!parentItem())
+ qmlWarning(this) << "Expected parent item by component completion!";
+
+ m_largestHeight = implicitHeight();
+ if (m_largestHeight > 0) {
+ emit largestHeightChanged();
+ } else {
+ qmlWarning(this) << "Expected implicitHeight of placeholder text" << text()
+ << "to be greater than 0 by component completion!";
+ }
+
+ maybeSetFocusAnimationProgress();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h
new file mode 100644
index 0000000000..525e079c28
--- /dev/null
+++ b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h
@@ -0,0 +1,94 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKMATERIALPLACEHOLDERTEXT_P_H
+#define QQUICKMATERIALPLACEHOLDERTEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qglobal_p.h>
+#include <QtGui/qcolor.h>
+#include <QtQuickControls2Impl/private/qquickplaceholdertext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QParallelAnimationGroup;
+
+class QQuickMaterialPlaceholderText : public QQuickPlaceholderText
+{
+ Q_OBJECT
+ Q_PROPERTY(bool filled READ isFilled WRITE setFilled NOTIFY filledChanged FINAL)
+ Q_PROPERTY(bool controlHasActiveFocus READ controlHasActiveFocus
+ WRITE setControlHasActiveFocus NOTIFY controlHasActiveFocusChanged FINAL)
+ Q_PROPERTY(bool controlHasText READ controlHasText WRITE setControlHasText NOTIFY controlHasTextChanged FINAL)
+ Q_PROPERTY(int largestHeight READ largestHeight NOTIFY largestHeightChanged FINAL)
+ Q_PROPERTY(qreal verticalPadding READ verticalPadding WRITE setVerticalPadding NOTIFY verticalPaddingChanged FINAL)
+ Q_PROPERTY(qreal controlImplicitBackgroundHeight READ controlImplicitBackgroundHeight
+ WRITE setControlImplicitBackgroundHeight NOTIFY controlImplicitBackgroundHeightChanged FINAL)
+ QML_NAMED_ELEMENT(FloatingPlaceholderText)
+ QML_ADDED_IN_VERSION(6, 5)
+
+public:
+ explicit QQuickMaterialPlaceholderText(QQuickItem *parent = nullptr);
+
+ bool isFilled() const;
+ void setFilled(bool filled);
+
+ int largestHeight() const;
+
+ bool controlHasActiveFocus() const;
+ void setControlHasActiveFocus(bool controlHasActiveFocus);
+
+ bool controlHasText() const;
+ void setControlHasText(bool controlHasText);
+
+ qreal controlImplicitBackgroundHeight() const;
+ void setControlImplicitBackgroundHeight(qreal controlImplicitBackgroundHeight);
+
+ qreal verticalPadding() const;
+ void setVerticalPadding(qreal verticalPadding);
+
+signals:
+ void filledChanged();
+ void largestHeightChanged();
+ void controlHasActiveFocusChanged();
+ void controlHasTextChanged();
+ void controlImplicitBackgroundHeightChanged();
+ void verticalPaddingChanged();
+
+private:
+ bool shouldFloat() const;
+ bool shouldAnimate() const;
+
+ qreal normalTargetY() const;
+ qreal floatingTargetY() const;
+
+ void controlGotActiveFocus();
+ void controlLostActiveFocus();
+
+ void maybeSetFocusAnimationProgress();
+
+ void componentComplete() override;
+
+ bool m_filled = false;
+ bool m_controlHasActiveFocus = false;
+ bool m_controlHasText = false;
+ int m_largestHeight = 0;
+ qreal m_verticalPadding = 0;
+ qreal m_controlImplicitBackgroundHeight = 0;
+ QPointer<QParallelAnimationGroup> m_focusInAnimation;
+ QPointer<QParallelAnimationGroup> m_focusOutAnimation;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKMATERIALPLACEHOLDERTEXT_P_H
diff --git a/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp b/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp
new file mode 100644
index 0000000000..2751efaa66
--- /dev/null
+++ b/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp
@@ -0,0 +1,385 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquickmaterialtextcontainer_p.h"
+
+#include <QtCore/qpropertyanimation.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qpainterpath.h>
+#include <QtQml/qqmlinfo.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ This class exists because:
+
+ - Rectangle doesn't support individual radii for each corner (QTBUG-48774).
+ - We need to draw an interrupted (where the placeholder text is) line for outlined containers.
+ - We need to animate the focus line for filled containers, and we can't use "Behavior on"
+ syntax because we only want to animate activeFocus becoming true, not also false. To do this
+ requires imperative code, and we want to keep the QML declarative.
+
+ focusAnimationProgress has to be a property even though it's only used internally,
+ because we have to use QPropertyAnimation on it.
+
+ An advantage of doing the animation in C++ is that we avoid the memory
+ overhead of an animation instance even when we're not using it, and instead
+ create it on demand and delete it when it's done. I tried doing the animation
+ declaratively with states and transitions, but it was more difficult to implement
+ and would have been harder to maintain, as well as having more overhead.
+*/
+
+QQuickMaterialTextContainer::QQuickMaterialTextContainer(QQuickItem *parent)
+ : QQuickPaintedItem(parent)
+{
+}
+
+bool QQuickMaterialTextContainer::isFilled() const
+{
+ return m_filled;
+}
+
+void QQuickMaterialTextContainer::setFilled(bool filled)
+{
+ if (filled == m_filled)
+ return;
+
+ m_filled = filled;
+ update();
+}
+
+QColor QQuickMaterialTextContainer::fillColor() const
+{
+ return m_fillColor;
+}
+
+void QQuickMaterialTextContainer::setFillColor(const QColor &fillColor)
+{
+ if (fillColor == m_fillColor)
+ return;
+
+ m_fillColor = fillColor;
+ update();
+}
+
+QColor QQuickMaterialTextContainer::outlineColor() const
+{
+ return m_outlineColor;
+}
+
+void QQuickMaterialTextContainer::setOutlineColor(const QColor &outlineColor)
+{
+ if (outlineColor == m_outlineColor)
+ return;
+
+ m_outlineColor = outlineColor;
+ update();
+}
+
+QColor QQuickMaterialTextContainer::focusedOutlineColor() const
+{
+ return m_outlineColor;
+}
+
+void QQuickMaterialTextContainer::setFocusedOutlineColor(const QColor &focusedOutlineColor)
+{
+ if (focusedOutlineColor == m_focusedOutlineColor)
+ return;
+
+ m_focusedOutlineColor = focusedOutlineColor;
+ update();
+}
+
+qreal QQuickMaterialTextContainer::focusAnimationProgress() const
+{
+ return m_focusAnimationProgress;
+}
+
+void QQuickMaterialTextContainer::setFocusAnimationProgress(qreal progress)
+{
+ if (qFuzzyCompare(progress, m_focusAnimationProgress))
+ return;
+
+ m_focusAnimationProgress = progress;
+ update();
+}
+
+qreal QQuickMaterialTextContainer::placeholderTextWidth() const
+{
+ return m_placeholderTextWidth;
+}
+
+void QQuickMaterialTextContainer::setPlaceholderTextWidth(qreal placeholderTextWidth)
+{
+ if (qFuzzyCompare(placeholderTextWidth, m_placeholderTextWidth))
+ return;
+
+ m_placeholderTextWidth = placeholderTextWidth;
+ update();
+}
+
+bool QQuickMaterialTextContainer::controlHasActiveFocus() const
+{
+ return m_controlHasActiveFocus;
+}
+
+void QQuickMaterialTextContainer::setControlHasActiveFocus(bool controlHasActiveFocus)
+{
+ if (m_controlHasActiveFocus == controlHasActiveFocus)
+ return;
+
+ m_controlHasActiveFocus = controlHasActiveFocus;
+ if (m_controlHasActiveFocus)
+ controlGotActiveFocus();
+ else
+ controlLostActiveFocus();
+ emit controlHasActiveFocusChanged();
+}
+
+bool QQuickMaterialTextContainer::controlHasText() const
+{
+ return m_controlHasText;
+}
+
+void QQuickMaterialTextContainer::setControlHasText(bool controlHasText)
+{
+ if (m_controlHasText == controlHasText)
+ return;
+
+ m_controlHasText = controlHasText;
+ // TextArea's text length is updated after component completion,
+ // so account for that here and in setPlaceholderHasText().
+ maybeSetFocusAnimationProgress();
+ update();
+ emit controlHasTextChanged();
+}
+
+bool QQuickMaterialTextContainer::placeholderHasText() const
+{
+ return m_placeholderHasText;
+}
+
+void QQuickMaterialTextContainer::setPlaceholderHasText(bool placeholderHasText)
+{
+ if (m_placeholderHasText == placeholderHasText)
+ return;
+
+ m_placeholderHasText = placeholderHasText;
+ maybeSetFocusAnimationProgress();
+ update();
+ emit placeholderHasTextChanged();
+}
+
+int QQuickMaterialTextContainer::horizontalPadding() const
+{
+ return m_horizontalPadding;
+}
+
+/*!
+ \internal
+
+ The text field's horizontal padding.
+
+ We need this to be a property so that the QML can set it, since we can't
+ access QQuickMaterialStyle's C++ API from this plugin.
+*/
+void QQuickMaterialTextContainer::setHorizontalPadding(int horizontalPadding)
+{
+ if (m_horizontalPadding == horizontalPadding)
+ return;
+ m_horizontalPadding = horizontalPadding;
+ update();
+ emit horizontalPaddingChanged();
+}
+
+void QQuickMaterialTextContainer::paint(QPainter *painter)
+{
+ qreal w = width();
+ qreal h = height();
+ if (w <= 0 || h <= 0)
+ return;
+
+ // Account for pen width.
+ const qreal penWidth = m_filled ? 1 : (m_controlHasActiveFocus ? 2 : 1);
+ w -= penWidth;
+ h -= penWidth;
+
+ const qreal cornerRadius = 4;
+ // This is coincidentally the same as cornerRadius, but use different variable names
+ // to keep the code understandable.
+ const qreal gapPadding = 4;
+ QPainterPath path;
+
+ QPointF startPos;
+
+ // Top-left rounded corner.
+ 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.
+ // Changing the cap style to Qt::FlatCap would only fix this by half the pen width,
+ // but it has no effect anyway (perhaps it literally only affects end points and not "start" points?).
+ startPos = QPointF(gapCenterX - (m_focusAnimationProgress * halfPlaceholderWidth) - gapPadding - penWidth, 0);
+ }
+ path.moveTo(startPos);
+ path.arcTo(0, 0, cornerRadius * 2, cornerRadius * 2, 90, 90);
+
+ // Bottom-left corner.
+ if (m_filled) {
+ path.lineTo(0, h);
+ } else {
+ path.lineTo(0, h - cornerRadius * 2);
+ path.arcTo(0, h - cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 180, 90);
+ }
+
+ // Bottom-right corner.
+ if (m_filled) {
+ path.lineTo(w, h);
+ } else {
+ path.lineTo(w - cornerRadius * 2, h);
+ path.arcTo(w - cornerRadius * 2, h - cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 270, 90);
+ }
+
+ // Top-right rounded corner.
+ path.lineTo(w, cornerRadius);
+ path.arcTo(w - (cornerRadius * 2), 0, cornerRadius * 2, cornerRadius * 2, 0, 90);
+
+ if (m_filled || qFuzzyIsNull(m_focusAnimationProgress)) {
+ // 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());
+ }
+
+ // Account for pen width.
+ painter->translate(penWidth / 2, penWidth / 2);
+
+ painter->setRenderHint(QPainter::Antialiasing, true);
+
+ const bool focused = parentItem() && parentItem()->hasActiveFocus();
+ // We still want to draw the stroke when it's filled, otherwise it will be a pixel
+ // (the pen width) too narrow on either side.
+ QPen pen;
+ pen.setColor(m_filled ? m_fillColor : (focused ? m_focusedOutlineColor : m_outlineColor));
+ pen.setWidthF(penWidth);
+ painter->setPen(pen);
+ if (m_filled)
+ painter->setBrush(QBrush(m_fillColor));
+
+ // Fill or stroke the container's shape.
+ // If not filling, the default brush will be used, which is Qt::NoBrush.
+ painter->drawPath(path);
+
+ // Draw the focus line at the bottom for filled containers.
+ if (m_filled) {
+ if (!qFuzzyCompare(m_focusAnimationProgress, 1.0)) {
+ // Draw the enabled active indicator line (#10) that's at the bottom when it's not focused:
+ // https://m3.material.io/components/text-fields/specs#6d654d1d-262e-4697-858c-9a75e8e7c81d
+ // Don't bother drawing it when the animation has finished, as the focused active indicator
+ // line below will obscure it.
+ pen.setColor(m_outlineColor);
+ painter->setPen(pen);
+ painter->drawLine(0, h, w, h);
+ }
+
+ if (!qFuzzyIsNull(m_focusAnimationProgress)) {
+ // Draw the focused active indicator line (#6) that's at the bottom when it's focused.
+ // Start at the center and expand outwards.
+ const int lineLength = m_focusAnimationProgress * w;
+ const int horizontalCenter = w / 2;
+ pen.setColor(m_focusedOutlineColor);
+ pen.setWidth(2);
+ painter->setPen(pen);
+ painter->drawLine(horizontalCenter - (lineLength / 2), h,
+ horizontalCenter + (lineLength / 2) + pen.width() / 2, h);
+ }
+ }
+}
+
+bool QQuickMaterialTextContainer::shouldAnimateOutline() const
+{
+ return !m_controlHasText && m_placeholderHasText;
+}
+
+void QQuickMaterialTextContainer::controlGotActiveFocus()
+{
+ const bool shouldAnimate = m_filled ? !m_controlHasText : shouldAnimateOutline();
+ if (!shouldAnimate) {
+ // It does have focus, but sometimes we don't need to animate anything, just change colors.
+ if (m_filled && m_controlHasText) {
+ // When a filled container has text already entered, we should just immediately change
+ // the color and thickness of the indicator line.
+ m_focusAnimationProgress = 1;
+ }
+ update();
+ return;
+ }
+
+ startFocusAnimation();
+}
+
+void QQuickMaterialTextContainer::controlLostActiveFocus()
+{
+ // We don't want to animate the active indicator line (at the bottom) of filled containers
+ // when the control loses focus, only when it gets it.
+ if (m_filled || !shouldAnimateOutline()) {
+ // Ensure that we set this so that filled containers go back to a non-accent-colored
+ // active indicator line when losing focus.
+ if (m_filled)
+ m_focusAnimationProgress = 0;
+ update();
+ return;
+ }
+
+ QPropertyAnimation *animation = new QPropertyAnimation(this, "focusAnimationProgress", this);
+ animation->setDuration(300);
+ animation->setStartValue(1);
+ animation->setEndValue(0);
+ animation->start(QAbstractAnimation::DeleteWhenStopped);
+}
+
+void QQuickMaterialTextContainer::startFocusAnimation()
+{
+ // Each time setFocusAnimationProgress is called by the animation, it'll call update(),
+ // which will cause us to be re-rendered.
+ QPropertyAnimation *animation = new QPropertyAnimation(this, "focusAnimationProgress", this);
+ animation->setDuration(300);
+ animation->setStartValue(0);
+ animation->setEndValue(1);
+ animation->start(QAbstractAnimation::DeleteWhenStopped);
+}
+
+void QQuickMaterialTextContainer::maybeSetFocusAnimationProgress()
+{
+ // Show the interrupted outline when there is text.
+ if (!m_filled && m_controlHasText && m_placeholderHasText)
+ setFocusAnimationProgress(1);
+}
+
+void QQuickMaterialTextContainer::componentComplete()
+{
+ QQuickPaintedItem::componentComplete();
+
+ if (!parentItem())
+ qmlWarning(this) << "Expected parent item by component completion!";
+
+ maybeSetFocusAnimationProgress();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quickcontrols/material/impl/qquickmaterialtextcontainer_p.h b/src/quickcontrols/material/impl/qquickmaterialtextcontainer_p.h
new file mode 100644
index 0000000000..40fcff148b
--- /dev/null
+++ b/src/quickcontrols/material/impl/qquickmaterialtextcontainer_p.h
@@ -0,0 +1,108 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQUICKMATERIALTEXTCONTAINER_P_H
+#define QQUICKMATERIALTEXTCONTAINER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qglobal_p.h>
+#include <QtGui/qcolor.h>
+#include <QtQuick/qquickpainteditem.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickMaterialTextContainer : public QQuickPaintedItem
+{
+ Q_OBJECT
+ Q_PROPERTY(bool filled READ isFilled WRITE setFilled FINAL)
+ Q_PROPERTY(bool controlHasActiveFocus READ controlHasActiveFocus
+ WRITE setControlHasActiveFocus NOTIFY controlHasActiveFocusChanged FINAL)
+ Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor FINAL)
+ Q_PROPERTY(QColor outlineColor READ outlineColor WRITE setOutlineColor FINAL)
+ 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(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)
+ QML_NAMED_ELEMENT(MaterialTextContainer)
+ QML_ADDED_IN_VERSION(6, 5)
+
+public:
+ explicit QQuickMaterialTextContainer(QQuickItem *parent = nullptr);
+
+ bool isFilled() const;
+ void setFilled(bool filled);
+
+ QColor fillColor() const;
+ void setFillColor(const QColor &fillColor);
+
+ QColor outlineColor() const;
+ void setOutlineColor(const QColor &outlineColor);
+
+ QColor focusedOutlineColor() const;
+ void setFocusedOutlineColor(const QColor &focusedOutlineColor);
+
+ qreal focusAnimationProgress() const;
+ void setFocusAnimationProgress(qreal progress);
+
+ qreal placeholderTextWidth() const;
+ void setPlaceholderTextWidth(qreal placeholderTextWidth);
+
+ bool controlHasActiveFocus() const;
+ void setControlHasActiveFocus(bool controlHasActiveFocus);
+
+ bool controlHasText() const;
+ void setControlHasText(bool controlHasText);
+
+ bool placeholderHasText() const;
+ void setPlaceholderHasText(bool placeholderHasText);
+
+ int horizontalPadding() const;
+ void setHorizontalPadding(int horizontalPadding);
+
+ void paint(QPainter *painter) override;
+
+signals:
+ void animateChanged();
+ void controlHasActiveFocusChanged();
+ void controlHasTextChanged();
+ void placeholderHasTextChanged();
+ void horizontalPaddingChanged();
+
+private:
+ bool shouldAnimateOutline() const;
+
+ void controlGotActiveFocus();
+ void controlLostActiveFocus();
+ void startFocusAnimation();
+
+ void maybeSetFocusAnimationProgress();
+
+ void componentComplete() override;
+
+ QColor m_fillColor;
+ QColor m_outlineColor;
+ QColor m_focusedOutlineColor;
+ qreal m_focusAnimationProgress = 0;
+ qreal m_placeholderTextWidth = 0;
+ bool m_filled = false;
+ bool m_controlHasActiveFocus = false;
+ bool m_controlHasText = false;
+ bool m_placeholderHasText = false;
+ int m_horizontalPadding = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKMATERIALTEXTCONTAINER_P_H
diff --git a/src/quickcontrols/material/qquickmaterialstyle.cpp b/src/quickcontrols/material/qquickmaterialstyle.cpp
index d7eb72f830..6fb4057080 100644
--- a/src/quickcontrols/material/qquickmaterialstyle.cpp
+++ b/src/quickcontrols/material/qquickmaterialstyle.cpp
@@ -422,6 +422,8 @@ static const QRgb switchDisabledCheckedTrackColorLight = 0x1E1C1B1F;
static const QRgb switchDisabledCheckedTrackColorDark = 0x1EE6E1E5;
static const QRgb switchDisabledUncheckedIconColorLight = 0x611C1B1F;
static const QRgb switchDisabledUncheckedIconColorDark = 0x61E6E1E5;
+static const QRgb textFieldFilledContainerColorLight = 0xFFE7E0EC;
+static const QRgb textFieldFilledContainerColorDark = 0xFF49454F;
static QQuickMaterialStyle::Theme effectiveTheme(QQuickMaterialStyle::Theme theme)
{
@@ -1158,6 +1160,11 @@ QColor QQuickMaterialStyle::sliderDisabledColor() const
return QColor::fromRgba(m_theme == Light ? sliderDisabledColorLight : sliderDisabledColorDark);
}
+QColor QQuickMaterialStyle::textFieldFilledContainerColor() const
+{
+ return QColor::fromRgba(m_theme == Light ? textFieldFilledContainerColorLight : textFieldFilledContainerColorDark);
+}
+
QColor QQuickMaterialStyle::color(QQuickMaterialStyle::Color color, QQuickMaterialStyle::Shade shade) const
{
int count = sizeof(colors) / sizeof(colors[0]);
@@ -1299,6 +1306,21 @@ int QQuickMaterialStyle::switchDelegateVerticalPadding() const
return globalVariant == Dense ? 4 : 8;
}
+int QQuickMaterialStyle::textFieldHeight() const
+{
+ // filled: https://m3.material.io/components/text-fields/specs#8c032848-e442-46df-b25d-28f1315f234b
+ // outlined: https://m3.material.io/components/text-fields/specs#605e24f1-1c1f-4c00-b385-4bf50733a5ef
+ return globalVariant == Dense ? 44 : 56;
+}
+int QQuickMaterialStyle::textFieldHorizontalPadding() const
+{
+ return globalVariant == Dense ? 12 : 16;
+}
+int QQuickMaterialStyle::textFieldVerticalPadding() const
+{
+ return globalVariant == Dense ? 4 : 8;
+}
+
int QQuickMaterialStyle::tooltipHeight() const
{
// https://material.io/guidelines/components/tooltips.html
diff --git a/src/quickcontrols/material/qquickmaterialstyle_p.h b/src/quickcontrols/material/qquickmaterialstyle_p.h
index ab3483f04d..83b9e3ede3 100644
--- a/src/quickcontrols/material/qquickmaterialstyle_p.h
+++ b/src/quickcontrols/material/qquickmaterialstyle_p.h
@@ -73,6 +73,7 @@ class QQuickMaterialStyle : public QQuickAttachedPropertyPropagator
Q_PROPERTY(QColor toolTextColor READ toolTextColor NOTIFY toolTextColorChanged FINAL)
Q_PROPERTY(QColor spinBoxDisabledIconColor READ spinBoxDisabledIconColor NOTIFY themeChanged FINAL)
Q_PROPERTY(QColor sliderDisabledColor READ sliderDisabledColor NOTIFY themeChanged FINAL REVISION(2, 15))
+ Q_PROPERTY(QColor textFieldFilledContainerColor READ textFieldFilledContainerColor NOTIFY themeChanged FINAL)
Q_PROPERTY(int touchTarget READ touchTarget CONSTANT FINAL)
Q_PROPERTY(int buttonHeight READ buttonHeight CONSTANT FINAL)
@@ -82,6 +83,9 @@ class QQuickMaterialStyle : public QQuickAttachedPropertyPropagator
Q_PROPERTY(int menuItemHeight READ menuItemHeight CONSTANT FINAL)
Q_PROPERTY(int menuItemVerticalPadding READ menuItemVerticalPadding CONSTANT FINAL)
Q_PROPERTY(int switchDelegateVerticalPadding READ switchDelegateVerticalPadding CONSTANT FINAL)
+ Q_PROPERTY(int textFieldHeight READ textFieldHeight CONSTANT FINAL)
+ Q_PROPERTY(int textFieldHorizontalPadding READ textFieldHorizontalPadding CONSTANT FINAL)
+ Q_PROPERTY(int textFieldVerticalPadding READ textFieldVerticalPadding CONSTANT FINAL)
Q_PROPERTY(int tooltipHeight READ tooltipHeight CONSTANT FINAL)
QML_NAMED_ELEMENT(Material)
@@ -254,6 +258,7 @@ public:
QColor toolTextColor() const;
QColor spinBoxDisabledIconColor() const;
QColor sliderDisabledColor() const;
+ QColor textFieldFilledContainerColor() const;
Q_INVOKABLE QColor color(Color color, Shade shade = Shade500) const;
Q_INVOKABLE QColor shade(const QColor &color, Shade shade) const;
@@ -266,6 +271,9 @@ public:
int menuItemHeight() const;
int menuItemVerticalPadding() const;
int switchDelegateVerticalPadding() const;
+ int textFieldHeight() const;
+ int textFieldHorizontalPadding() const;
+ int textFieldVerticalPadding() const;
int tooltipHeight() const;
static void initGlobals();
diff --git a/tests/auto/quickcontrols/controls/data/tst_textfield.qml b/tests/auto/quickcontrols/controls/data/tst_textfield.qml
index f5b3d91fe1..b334c9cd6d 100644
--- a/tests/auto/quickcontrols/controls/data/tst_textfield.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_textfield.qml
@@ -5,6 +5,7 @@ import QtQuick
import QtTest
import QtQuick.Controls
import QtQuick.Layouts
+import Qt.test.controls
TestCase {
id: testCase
@@ -157,16 +158,21 @@ TestCase {
if (data.textAlignment !== undefined)
compare(control.horizontalAlignment, data.textAlignment)
- for (var i = 0; i < control.children.length; ++i) {
- if (control.children[i].hasOwnProperty("text") && control.children[i].hasOwnProperty("horizontalAlignment"))
- compare(control.children[i].effectiveHorizontalAlignment, data.placeholderAlignment) // placeholder
+ // The placeholder text of the Material style doesn't currently respect the alignment of the control.
+ if (StyleInfo.styleName !== "Material") {
+ for (var i = 0; i < control.children.length; ++i) {
+ if (control.children[i].hasOwnProperty("text") && control.children[i].hasOwnProperty("horizontalAlignment"))
+ compare(control.children[i].effectiveHorizontalAlignment, data.placeholderAlignment) // placeholder
+ }
}
control.verticalAlignment = TextField.AlignBottom
compare(control.verticalAlignment, TextField.AlignBottom)
- for (var j = 0; j < control.children.length; ++j) {
- if (control.children[j].hasOwnProperty("text") && control.children[j].hasOwnProperty("verticalAlignment"))
- compare(control.children[j].verticalAlignment, Text.AlignBottom) // placeholder
+ if (StyleInfo.styleName !== "Material") {
+ for (var j = 0; j < control.children.length; ++j) {
+ if (control.children[j].hasOwnProperty("text") && control.children[j].hasOwnProperty("verticalAlignment"))
+ compare(control.children[j].verticalAlignment, Text.AlignBottom) // placeholder
+ }
}
}
diff --git a/tests/auto/quickcontrols/qquicktextfield/tst_qquicktextfield.cpp b/tests/auto/quickcontrols/qquicktextfield/tst_qquicktextfield.cpp
index 19219bb79e..1cfa1663ef 100644
--- a/tests/auto/quickcontrols/qquicktextfield/tst_qquicktextfield.cpp
+++ b/tests/auto/quickcontrols/qquicktextfield/tst_qquicktextfield.cpp
@@ -84,9 +84,9 @@ void tst_QQuickTextField::touchscreenDoesNotSelect()
if (selectByMouse) {
// press-drag-and-release from x1 to x2
- int x1 = 10;
- int x2 = 70;
- int y = QFontMetrics(textField->font()).height() / 2;
+ const int x1 = textField->leftPadding();
+ const int x2 = textField->width() / 2;
+ const int y = textField->height() / 2;
QTest::touchEvent(&window, touchDevice.data()).press(0, QPoint(x1,y), &window);
QTest::touchEvent(&window, touchDevice.data()).move(0, QPoint(x2,y), &window);
QTest::touchEvent(&window, touchDevice.data()).release(0, QPoint(x2,y), &window);
diff --git a/tests/manual/quickcontrols/material/CMakeLists.txt b/tests/manual/quickcontrols/material/CMakeLists.txt
index 004d3e8708..2564f341f1 100644
--- a/tests/manual/quickcontrols/material/CMakeLists.txt
+++ b/tests/manual/quickcontrols/material/CMakeLists.txt
@@ -24,6 +24,7 @@ set(qmake_immediate_resource_files
"pages/DelayButtonPage.qml"
"pages/RoundButtonPage.qml"
"pages/SwitchPage.qml"
+ "pages/TextFieldPage.qml"
"qmldir"
)
diff --git a/tests/manual/quickcontrols/material/material.qml b/tests/manual/quickcontrols/material/material.qml
index 78efaf72e3..95aedf17f3 100644
--- a/tests/manual/quickcontrols/material/material.qml
+++ b/tests/manual/quickcontrols/material/material.qml
@@ -97,7 +97,7 @@ ApplicationWindow {
focus: true
currentIndex: settings.currentControlIndex
anchors.fill: parent
- model: ["Button", "DelayButton", "RoundButton", "Switch"]
+ model: ["Button", "DelayButton", "RoundButton", "Switch", "TextField"]
delegate: ItemDelegate {
width: listView.width
text: modelData
diff --git a/tests/manual/quickcontrols/material/pages/TextFieldPage.qml b/tests/manual/quickcontrols/material/pages/TextFieldPage.qml
new file mode 100644
index 0000000000..4890047a78
--- /dev/null
+++ b/tests/manual/quickcontrols/material/pages/TextFieldPage.qml
@@ -0,0 +1,86 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import QtQuick
+import QtQuick.Controls.Material
+import QtQuick.Layouts
+
+import ".."
+
+Page {
+ topPadding: Constants.pageTopPadding
+
+ component TextFieldFlow: Flow {
+ id: layout
+ spacing: 40
+
+ required property int containerStyle
+
+ TextField {
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextField {
+ placeholderText: "placeholderText"
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextField {
+ text: "text"
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextField {
+ text: "text"
+ placeholderText: "placeholderText"
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextField {
+ placeholderText: "Disabled placeholder"
+ enabled: false
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextField {
+ text: "Disabled text"
+ enabled: false
+
+ Material.containerStyle: layout.containerStyle
+ }
+
+ TextField {
+ text: "text"
+ placeholderText: "placeholderText"
+ enabled: false
+
+ Material.containerStyle: layout.containerStyle
+ }
+ }
+
+ ColumnLayout {
+ width: parent.width
+
+ Label {
+ text: "Filled"
+ }
+ TextFieldFlow {
+ containerStyle: Material.Filled
+
+ Layout.fillWidth: true
+ Layout.bottomMargin: 40
+ }
+
+ Label {
+ text: "Outlined"
+ }
+ TextFieldFlow {
+ containerStyle: Material.Outlined
+
+ Layout.fillWidth: true
+ }
+ }
+}