From 773ec5dcfe6c9d7c5c1df3e236a03988c719ef6c Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Tue, 1 Oct 2024 12:30:58 +0200 Subject: [PATCH 01/11] text input: Introduce Selected state and rename Text --- designsystems/fluent2/QskFluent2Skin.cpp | 10 ++--- designsystems/fusion/QskFusionSkin.cpp | 9 ++--- designsystems/material3/QskMaterial3Skin.cpp | 8 ++-- playground/gradients/main.cpp | 4 +- playground/inputpanel/main.cpp | 8 ++-- src/controls/QskTextInput.cpp | 41 +++++++++----------- src/controls/QskTextInput.h | 14 +++---- src/controls/QskTextInputSkinlet.cpp | 2 +- src/inputpanel/QskInputPanelBox.cpp | 4 +- 9 files changed, 47 insertions(+), 53 deletions(-) diff --git a/designsystems/fluent2/QskFluent2Skin.cpp b/designsystems/fluent2/QskFluent2Skin.cpp index 10f73b74..caf41258 100644 --- a/designsystems/fluent2/QskFluent2Skin.cpp +++ b/designsystems/fluent2/QskFluent2Skin.cpp @@ -1773,8 +1773,8 @@ void Editor::setupTextInputMetrics() setBoxShape( Q::Panel, 3_px ); - setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter ); - setFontRole( Q::Text, Fluent2::Body ); + setAlignment( Q::InputText, Qt::AlignLeft | Qt::AlignVCenter ); + setFontRole( Q::InputText, Fluent2::Body ); } void Editor::setupTextInputColors( @@ -1785,8 +1785,8 @@ void Editor::setupTextInputColors( const auto& pal = theme.palette; - setColor( Q::PanelSelected, pal.fillColor.accent.selectedTextBackground ); - setColor( Q::TextSelected, pal.fillColor.textOnAccent.selectedText ); + setColor( Q::Panel | Q::Selected, pal.fillColor.accent.selectedTextBackground ); + setColor( Q::InputText | Q::Selected, pal.fillColor.textOnAccent.selectedText ); for( const auto state : { A::NoState, Q::Hovered, Q::Focused, Q::Editing, Q::Disabled } ) { @@ -1821,7 +1821,7 @@ void Editor::setupTextInputColors( } const auto panel = Q::Panel | section | state; - const auto text = Q::Text | section | state; + const auto text = Q::InputText | section | state; panelColor = rgbSolid( panelColor, pal.background.solid.base ); diff --git a/designsystems/fusion/QskFusionSkin.cpp b/designsystems/fusion/QskFusionSkin.cpp index bad14860..1ad74048 100644 --- a/designsystems/fusion/QskFusionSkin.cpp +++ b/designsystems/fusion/QskFusionSkin.cpp @@ -386,18 +386,17 @@ void Editor::setupTextInput() using A = QskAspect; using P = QPalette; - setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignTop ); + setAlignment( Q::InputText, Qt::AlignLeft | Qt::AlignTop ); for ( auto state : { A::NoState, Q::Disabled } ) { const auto colorGroup = ( state == A::NoState ) ? P::Active : P::Disabled; setGradient( Q::Panel | state, m_pal.color( colorGroup, P::Base ) ); - setColor( Q::PanelSelected | state, m_pal.color( colorGroup, P::Highlight ) ); - - setColor( Q::Text | state, m_pal.color( colorGroup, P::Text ) ); - setColor( Q::TextSelected | state, m_pal.color( colorGroup, P::HighlightedText ) ); + setColor( Q::Panel | Q::Selected | state, m_pal.color( colorGroup, P::Highlight ) ); + setColor( Q::InputText | state, m_pal.color( colorGroup, P::Text ) ); + setColor( Q::InputText | Q::Selected | state, m_pal.color( colorGroup, P::HighlightedText ) ); } setBoxBorderMetrics( Q::Panel, 1_px ); diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index 22d662bf..608f48fe 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -470,15 +470,15 @@ void Editor::setupTextInput() // ### Also add a pressed state - setColor( Q::Text, m_pal.onSurface ); - setFontRole( Q::Text, BodyMedium ); - setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter ); + setColor( Q::InputText, m_pal.onSurface ); + setFontRole( Q::InputText, BodyMedium ); + setAlignment( Q::InputText, Qt::AlignLeft | Qt::AlignVCenter ); const auto disabledPanelColor = QskRgb::toTransparentF( m_pal.onSurface, 0.04 ); setGradient( Q::Panel | Q::Disabled, disabledPanelColor ); setBoxBorderColors( Q::Panel | Q::Disabled, m_pal.onSurface38 ); - setColor( Q::Text | Q::Disabled, m_pal.onSurface38 ); + setColor( Q::InputText | Q::Disabled, m_pal.onSurface38 ); } void Editor::setupProgressBar() diff --git a/playground/gradients/main.cpp b/playground/gradients/main.cpp index e952c8f2..46a0b6e5 100644 --- a/playground/gradients/main.cpp +++ b/playground/gradients/main.cpp @@ -52,7 +52,7 @@ namespace m_input = new QskTextInput( this ); m_input->setValidator( new InputValidator( m_input ) ); - m_input->setText( QString::number( value ) ); + m_input->setInputText( QString::number( value ) ); const QFontMetricsF fm( m_input->font() ); m_input->setFixedWidth( fm.horizontalAdvance( "-0.000" ) ); @@ -65,7 +65,7 @@ namespace qreal value() const { - return m_input->text().toDouble(); + return m_input->inputText().toDouble(); } Q_SIGNALS: diff --git a/playground/inputpanel/main.cpp b/playground/inputpanel/main.cpp index eabc5362..af3f46a4 100644 --- a/playground/inputpanel/main.cpp +++ b/playground/inputpanel/main.cpp @@ -202,24 +202,24 @@ class InputBox : public QskLinearBox setSpacing( 10 ); auto* textInput1 = new QskTextInput( this ); - textInput1->setText( "Press and edit Me." ); + textInput1->setInputText( "Press and edit Me." ); textInput1->setSizePolicy( Qt::Horizontal, QskSizePolicy::Preferred ); auto* textInput2 = new QskTextInput( this ); - textInput2->setText( "Press and edit Me." ); + textInput2->setInputText( "Press and edit Me." ); textInput2->setSizePolicy( Qt::Horizontal, QskSizePolicy::Preferred ); textInput2->setActivationModes( QskTextInput::ActivationOnAll ); auto* textInput3 = new QskTextInput( this ); textInput3->setReadOnly( true ); - textInput3->setText( "Read Only information." ); + textInput3->setInputText( "Read Only information." ); textInput3->setSizePolicy( Qt::Horizontal, QskSizePolicy::Preferred ); auto* textInput4 = new QskTextInput( this ); textInput4->setEchoMode( QskTextInput::Password ); textInput4->setPasswordMaskDelay( 1000 ); textInput4->setMaxLength( 8 ); - textInput4->setText( "12345678" ); + textInput4->setInputText( "12345678" ); textInput4->setSizePolicy( Qt::Horizontal, QskSizePolicy::Preferred ); } }; diff --git a/src/controls/QskTextInput.cpp b/src/controls/QskTextInput.cpp index a4982bcc..fef21940 100644 --- a/src/controls/QskTextInput.cpp +++ b/src/controls/QskTextInput.cpp @@ -13,16 +13,11 @@ QSK_QT_PRIVATE_BEGIN QSK_QT_PRIVATE_END QSK_SUBCONTROL( QskTextInput, Panel ) -QSK_SUBCONTROL( QskTextInput, Text ) - -#if 1 -// shouldn't this be a Selected state, TODO ... -QSK_SUBCONTROL( QskTextInput, PanelSelected ) -QSK_SUBCONTROL( QskTextInput, TextSelected ) -#endif +QSK_SUBCONTROL( QskTextInput, InputText ) QSK_SYSTEM_STATE( QskTextInput, ReadOnly, QskAspect::FirstSystemState << 1 ) QSK_SYSTEM_STATE( QskTextInput, Editing, QskAspect::FirstSystemState << 2 ) +QSK_SYSTEM_STATE( QskTextInput, Selected, QskAspect::FirstSystemState << 3 ) static inline void qskPropagateReadOnly( QskTextInput* input ) { @@ -36,13 +31,13 @@ static inline void qskBindSignals( const QQuickTextInput* wrappedInput, QskTextInput* input ) { QObject::connect( wrappedInput, &QQuickTextInput::textChanged, - input, [ input ] { Q_EMIT input->textChanged( input->text() ); } ); + input, [ input ] { Q_EMIT input->inputTextChanged( input->inputText() ); } ); QObject::connect( wrappedInput, &QQuickTextInput::displayTextChanged, input, [ input ] { Q_EMIT input->displayTextChanged( input->displayText() ); } ); QObject::connect( wrappedInput, &QQuickTextInput::textEdited, - input, [ input ] { Q_EMIT input->textEdited( input->text() ); } ); + input, [ input ] { Q_EMIT input->textEdited( input->inputText() ); } ); QObject::connect( wrappedInput, &QQuickTextInput::validatorChanged, input, &QskTextInput::validatorChanged ); @@ -252,7 +247,7 @@ namespace QColor color; - color = input->color( QskTextInput::Text ); + color = input->color( QskTextInput::InputText ); if ( d->color != color ) { d->color = color; @@ -261,14 +256,14 @@ namespace if ( d->hasSelectedText() ) { - color = input->color( QskTextInput::PanelSelected ); + color = input->color( QskTextInput::Panel | QskTextInput::Selected ); if ( d->selectionColor != color ) { d->selectionColor = color; isDirty = true; } - color = input->color( QskTextInput::TextSelected ); + color = input->color( QskTextInput::InputText | QskTextInput::Selected ); if ( d->selectedTextColor != color ) { d->selectedTextColor = color; @@ -519,7 +514,7 @@ void QskTextInput::updateLayout() auto input = m_data->textInput; input->updateMetrics(); - qskSetItemGeometry( input, subControlRect( Text ) ); + qskSetItemGeometry( input, subControlRect( InputText ) ); } void QskTextInput::updateNode( QSGNode* node ) @@ -528,12 +523,12 @@ void QskTextInput::updateNode( QSGNode* node ) Inherited::updateNode( node ); } -QString QskTextInput::text() const +QString QskTextInput::inputText() const { return m_data->textInput->text(); } -void QskTextInput::setText( const QString& text ) +void QskTextInput::setInputText( const QString& text ) { m_data->textInput->setText( text ); } @@ -574,7 +569,7 @@ static inline void qskUpdateInputMethodFont( const QskTextInput* input ) void QskTextInput::setFontRole( const QskFontRole& role ) { - if ( setFontRoleHint( Text, role ) ) + if ( setFontRoleHint( InputText, role ) ) { qskUpdateInputMethodFont( this ); Q_EMIT fontRoleChanged(); @@ -583,7 +578,7 @@ void QskTextInput::setFontRole( const QskFontRole& role ) void QskTextInput::resetFontRole() { - if ( resetFontRoleHint( Text ) ) + if ( resetFontRoleHint( InputText ) ) { qskUpdateInputMethodFont( this ); Q_EMIT fontRoleChanged(); @@ -592,12 +587,12 @@ void QskTextInput::resetFontRole() QskFontRole QskTextInput::fontRole() const { - return fontRoleHint( Text ); + return fontRoleHint( InputText ); } void QskTextInput::setAlignment( Qt::Alignment alignment ) { - if ( setAlignmentHint( Text, alignment ) ) + if ( setAlignmentHint( InputText, alignment ) ) { m_data->textInput->setAlignment( alignment ); Q_EMIT alignmentChanged(); @@ -606,7 +601,7 @@ void QskTextInput::setAlignment( Qt::Alignment alignment ) void QskTextInput::resetAlignment() { - if ( resetAlignmentHint( Text ) ) + if ( resetAlignmentHint( InputText ) ) { m_data->textInput->setAlignment( alignment() ); Q_EMIT alignmentChanged(); @@ -615,7 +610,7 @@ void QskTextInput::resetAlignment() Qt::Alignment QskTextInput::alignment() const { - return alignmentHint( Text, Qt::AlignLeft | Qt::AlignTop ); + return alignmentHint( InputText, Qt::AlignLeft | Qt::AlignTop ); } void QskTextInput::setWrapMode( QskTextOptions::WrapMode wrapMode ) @@ -632,7 +627,7 @@ QskTextOptions::WrapMode QskTextInput::wrapMode() const QFont QskTextInput::font() const { - return effectiveFont( QskTextInput::Text ); + return effectiveFont( QskTextInput::InputText ); } bool QskTextInput::isReadOnly() const @@ -922,7 +917,7 @@ void QskTextInput::setupFrom( const QQuickItem* item ) if ( event.queries() & Qt::ImSurroundingText ) { const auto text = event.value( Qt::ImSurroundingText ).toString(); - setText( text ); + setInputText( text ); } if ( event.queries() & Qt::ImCursorPosition ) diff --git a/src/controls/QskTextInput.h b/src/controls/QskTextInput.h index 3aae8c05..de8aa429 100644 --- a/src/controls/QskTextInput.h +++ b/src/controls/QskTextInput.h @@ -16,7 +16,7 @@ class QSK_EXPORT QskTextInput : public QskControl { Q_OBJECT - Q_PROPERTY( QString text READ text WRITE setText NOTIFY textChanged USER true) + Q_PROPERTY( QString inputText READ inputText WRITE setInputText NOTIFY inputTextChanged USER true ) Q_PROPERTY( QString description READ description WRITE setDescription NOTIFY descriptionChanged ) @@ -55,8 +55,8 @@ class QSK_EXPORT QskTextInput : public QskControl using Inherited = QskControl; public: - QSK_SUBCONTROLS( Panel, Text, PanelSelected, TextSelected ) - QSK_STATES( ReadOnly, Editing ) + QSK_SUBCONTROLS( Panel, InputText ) + QSK_STATES( ReadOnly, Editing, Selected ) enum ActivationMode { @@ -84,13 +84,13 @@ class QSK_EXPORT QskTextInput : public QskControl Q_ENUM( EchoMode ) QskTextInput( QQuickItem* parent = nullptr ); - QskTextInput( const QString& text, QQuickItem* parent = nullptr ); + QskTextInput( const QString&, QQuickItem* parent = nullptr ); ~QskTextInput() override; void setupFrom( const QQuickItem* ); - QString text() const; + QString inputText() const; void setDescription( const QString& ); QString description() const; @@ -163,7 +163,7 @@ class QSK_EXPORT QskTextInput : public QskControl void ensureVisible( int position ); public Q_SLOTS: - void setText( const QString& ); + void setInputText( const QString& ); void setEditing( bool ); Q_SIGNALS: @@ -173,7 +173,7 @@ class QSK_EXPORT QskTextInput : public QskControl void readOnlyChanged( bool ); void panelChanged( bool ); - void textChanged( const QString& ); + void inputTextChanged( const QString& ); void displayTextChanged( const QString& ); void textEdited( const QString& ); diff --git a/src/controls/QskTextInputSkinlet.cpp b/src/controls/QskTextInputSkinlet.cpp index 8f0e1b4c..027cce92 100644 --- a/src/controls/QskTextInputSkinlet.cpp +++ b/src/controls/QskTextInputSkinlet.cpp @@ -23,7 +23,7 @@ QRectF QskTextInputSkinlet::subControlRect( const QskSkinnable* skinnable, { return contentsRect; } - else if ( subControl == QskTextInput::Text ) + else if ( subControl == QskTextInput::InputText ) { return skinnable->subControlContentsRect( contentsRect, QskTextInput::Panel ); } diff --git a/src/inputpanel/QskInputPanelBox.cpp b/src/inputpanel/QskInputPanelBox.cpp index 46cda216..90991d0f 100644 --- a/src/inputpanel/QskInputPanelBox.cpp +++ b/src/inputpanel/QskInputPanelBox.cpp @@ -35,7 +35,7 @@ namespace if ( subControl == QskTextInput::Panel ) return m_panelBox->effectiveSubcontrol( QskInputPanelBox::ProxyPanel ); - if ( subControl == QskTextInput::Text ) + if ( subControl == QskTextInput::InputText ) return m_panelBox->effectiveSubcontrol( QskInputPanelBox::ProxyText ); return subControl; @@ -187,7 +187,7 @@ QskAspect::Subcontrol QskInputPanelBox::substitutedSubcontrol( return QskTextInput::Panel; if ( subControl == QskInputPanelBox::ProxyText ) - return QskTextInput::Text; + return QskTextInput::InputText; #endif return subControl; From b88b3123d7ba87e0d2ccd60c48e6d285a97de0f2 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Fri, 4 Oct 2024 14:43:24 +0200 Subject: [PATCH 02/11] text input: Also update metrics when updating node We need to recalculate alignment etc. when changing states --- src/controls/QskTextInput.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controls/QskTextInput.cpp b/src/controls/QskTextInput.cpp index fef21940..352d5ef9 100644 --- a/src/controls/QskTextInput.cpp +++ b/src/controls/QskTextInput.cpp @@ -520,6 +520,7 @@ void QskTextInput::updateLayout() void QskTextInput::updateNode( QSGNode* node ) { m_data->textInput->updateColors(); + m_data->textInput->updateMetrics(); Inherited::updateNode( node ); } From 020857332539a98ac14b319e899a660df9e6414e Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Tue, 1 Oct 2024 12:32:05 +0200 Subject: [PATCH 03/11] text input: Add subcontrols needed for M3 and F2 --- designsystems/material3/QskMaterial3Icons.qrc | 3 + designsystems/material3/QskMaterial3Skin.cpp | 131 +++++++++-- designsystems/material3/QskMaterial3Skin.h | 2 + .../material3/icons/qvg/text_field_cancel.qvg | Bin 0 -> 1247 bytes .../material3/icons/qvg/text_field_error.qvg | Bin 0 -> 927 bytes .../material3/icons/qvg/text_field_search.qvg | Bin 0 -> 1107 bytes .../material3/icons/text_field_cancel.svg | 4 + .../material3/icons/text_field_error.svg | 4 + .../material3/icons/text_field_search.svg | 4 + examples/gallery/inputs/InputPage.cpp | 45 +++- src/controls/QskTextInput.cpp | 119 +++++++++- src/controls/QskTextInput.h | 44 +++- src/controls/QskTextInputSkinlet.cpp | 220 +++++++++++++++++- src/controls/QskTextInputSkinlet.h | 7 + 14 files changed, 530 insertions(+), 53 deletions(-) create mode 100644 designsystems/material3/icons/qvg/text_field_cancel.qvg create mode 100644 designsystems/material3/icons/qvg/text_field_error.qvg create mode 100644 designsystems/material3/icons/qvg/text_field_search.qvg create mode 100644 designsystems/material3/icons/text_field_cancel.svg create mode 100644 designsystems/material3/icons/text_field_error.svg create mode 100644 designsystems/material3/icons/text_field_search.svg diff --git a/designsystems/material3/QskMaterial3Icons.qrc b/designsystems/material3/QskMaterial3Icons.qrc index 72725b7a..2c44364a 100644 --- a/designsystems/material3/QskMaterial3Icons.qrc +++ b/designsystems/material3/QskMaterial3Icons.qrc @@ -17,5 +17,8 @@ icons/qvg/arrow_drop_up.qvg icons/qvg/check.qvg icons/qvg/remove.qvg + icons/qvg/text_field_search.qvg + icons/qvg/text_field_cancel.qvg + icons/qvg/text_field_error.qvg diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index 608f48fe..076850b6 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -452,33 +452,126 @@ void Editor::setupTextInput() { using Q = QskTextInput; - setStrutSize( Q::Panel, -1.0, 56_dp ); - setPadding( Q::Panel, { 12_dp, 8_dp, 12_dp, 8_dp } ); - setGradient( Q::Panel, m_pal.surfaceVariant ); - setBoxShape( Q::Panel, m_pal.shapeExtraSmallTop ); - setBoxBorderMetrics( Q::Panel, { 0, 0, 0, 1_dp } ); - setBoxBorderColors( Q::Panel, m_pal.onSurfaceVariant ); - setSpacing( Q::Panel, 8_dp ); + const QskStateCombination allStates( QskStateCombination::CombinationNoState, QskAspect::AllStates ); - const auto hoverColor = flattenedColor( m_pal.onSurfaceVariant, + // Panel + + setStrutSize( Q::Panel, -1.0, 56_dp ); + setGradient( Q::Panel, m_pal.surfaceVariant ); + + setBoxShape( Q::Panel, m_pal.shapeExtraSmallTop ); + + setBoxBorderMetrics( Q::Panel, { 0, 0, 0, 1_dp } ); + setBoxBorderMetrics( Q::Panel | Q::Focused, { 0, 0, 0, 2_dp }, allStates ); + + setBoxBorderColors( Q::Panel, m_pal.onSurfaceVariant ); + setBoxBorderColors( Q::Panel | Q::Error, m_pal.error, allStates ); + setBoxBorderColors( Q::Panel | Q::Error | Q::Hovered, m_pal.onErrorContainer, allStates ); + + const auto normalHoverColor = flattenedColor( m_pal.onSurfaceVariant, m_pal.surfaceVariant, m_pal.hoverOpacity ); - setGradient( Q::Panel | Q::Hovered, hoverColor ); + setGradient( Q::Panel | Q::Hovered, normalHoverColor, allStates ); + + const auto errorHoverColor = flattenedColor( m_pal.onSurface, + m_pal.surfaceVariant, m_pal.hoverOpacity ); + setGradient( Q::Panel | Q::Error | Q::Hovered, errorHoverColor, allStates ); const auto focusColor = flattenedColor( m_pal.onSurfaceVariant, m_pal.surfaceVariant, m_pal.focusOpacity ); - setGradient( Q::Panel | Q::Focused, focusColor ); - - // ### Also add a pressed state - - setColor( Q::InputText, m_pal.onSurface ); - setFontRole( Q::InputText, BodyMedium ); - setAlignment( Q::InputText, Qt::AlignLeft | Qt::AlignVCenter ); + setGradient( Q::Panel | Q::Focused, focusColor, allStates ); const auto disabledPanelColor = QskRgb::toTransparentF( m_pal.onSurface, 0.04 ); - setGradient( Q::Panel | Q::Disabled, disabledPanelColor ); - setBoxBorderColors( Q::Panel | Q::Disabled, m_pal.onSurface38 ); + setGradient( Q::Panel | Q::Disabled, disabledPanelColor, allStates ); + setBoxBorderColors( Q::Panel | Q::Disabled, m_pal.onSurface38, allStates ); + + + // LeadingIcon + + setStrutSize( Q::LeadingIcon, { 24_dp, 24_dp } ); + setMargin( Q::LeadingIcon, { 12_dp, 0, 0, 0 } ); + const auto leadingIcon = symbol( "text_field_search" ); + setSymbol( Q::LeadingIcon, leadingIcon ); + + setGraphicRole( Q::LeadingIcon, QskMaterial3Skin::GraphicRoleOnSurface ); + setGraphicRole( Q::LeadingIcon | Q::Error, QskMaterial3Skin::GraphicRoleOnSurfaceVariant, allStates ); + + + // LabelText + + setAlignment( Q::LabelText, Qt::AlignLeft | Qt::AlignVCenter ); + + const QskStateCombination textEmptyStates( QskStateCombination::CombinationNoState, + Q::Focused | Q::Hovered | Q::ReadOnly | Q::Disabled ); + + setFontRole( Q::LabelText | Q::TextEmpty, BodyMedium, textEmptyStates ); + setColor( Q::LabelText | Q::TextEmpty, m_pal.onSurfaceVariant, textEmptyStates ); + + const QskStateCombination editingHoveredFocused( QskStateCombination::CombinationNoState, + Q::Editing | Q::Hovered | Q::Focused ); + + setMargin( Q::LabelText, { 16_dp, 8_dp, 16_dp, 16_dp }, editingHoveredFocused ); + setColor( Q::LabelText, m_pal.primary, editingHoveredFocused ); + setFontRole( Q::LabelText, BodySmall, editingHoveredFocused ); + + setColor( Q::LabelText | Q::Error, m_pal.error, allStates ); + setColor( Q::LabelText | Q::Error | Q::Hovered, m_pal.onErrorContainer, allStates ); + + + // InputText + + setMargin( Q::InputText, { 16_dp, 8_dp, 16_dp, 8_dp } ); + setColor( Q::InputText, m_pal.onSurface ); + setFontRole( Q::InputText, BodyMedium ); + setAlignment( Q::InputText, Qt::AlignLeft | Qt::AlignBottom ); + + setColor( Q::InputText | Q::Error, m_pal.onSurface, allStates ); // same as with Hovered and Focused setColor( Q::InputText | Q::Disabled, m_pal.onSurface38 ); + + + // HintText + + setColor( Q::HintText, color( Q::InputText ) ); + setFontRole( Q::HintText, fontRole( Q::InputText ) ); + setAlignment( Q::HintText, alignment( Q::InputText ) ); + + + // TrailingIcon + + setStrutSize( Q::TrailingIcon, { 24_dp, 24_dp } ); + setMargin( Q::TrailingIcon, { 0, 0, 12_dp, 0 } ); + setGraphicRole( Q::TrailingIcon, QskMaterial3Skin::GraphicRoleOnSurface ); + const auto trailingIcon = symbol( "text_field_cancel" ); + setSymbol( Q::TrailingIcon, trailingIcon ); + + const auto errorIcon = symbol( "text_field_error" ); + setSymbol( Q::TrailingIcon | Q::Error, errorIcon, allStates ); + setGraphicRole( Q::TrailingIcon | Q::Error, QskMaterial3Skin::GraphicRoleError, allStates ); + setGraphicRole( Q::TrailingIcon | Q::Error | Q::Hovered, QskMaterial3Skin::GraphicRoleOnErrorContainer, allStates ); + + + // TrailingIconRipple + + setStrutSize( Q::TrailingIconRipple, { 45_dp, 45_dp } ); + setGradient( Q::TrailingIconRipple | Q::Hovered, m_pal.onSurface8, allStates ); + setBoxShape( Q::TrailingIconRipple, 100, Qt::RelativeSize ); + + + // SupportingText + + setMargin( Q::SupportingText, { 16_dp, 4_dp, 16_dp, 4_dp } ); + setColor( Q::SupportingText, m_pal.onSurfaceVariant ); + setColor( Q::SupportingText | Q::Error, m_pal.error, allStates ); + setFontRole( Q::SupportingText, BodySmall ); + setAlignment( Q::SupportingText, Qt::AlignLeft | Qt::AlignVCenter ); + + + // CharacterCount + + setMargin( Q::CharacterCount, margin( Q::SupportingText ) ); + setColor( Q::CharacterCount, color( Q::SupportingText ) ); + setFontRole( Q::CharacterCount, fontRole( Q::SupportingText ) ); + setAlignment( Q::CharacterCount, Qt::AlignRight | Qt::AlignVCenter ); } void Editor::setupProgressBar() @@ -1661,10 +1754,12 @@ void QskMaterial3Skin::setGraphicColor( GraphicRole role, QRgb rgb ) void QskMaterial3Skin::setupGraphicFilters( const QskMaterial3Theme& theme ) { + setGraphicColor( GraphicRoleError, theme.error ); setGraphicColor( GraphicRoleOnPrimary, theme.onPrimary ); setGraphicColor( GraphicRoleOnPrimaryContainer, theme.onPrimaryContainer ); setGraphicColor( GraphicRoleOnSecondaryContainer, theme.onSecondaryContainer ); setGraphicColor( GraphicRoleOnError, theme.onError ); + setGraphicColor( GraphicRoleOnErrorContainer, theme.onErrorContainer ); setGraphicColor( GraphicRoleOnSurface, theme.onSurface ); setGraphicColor( GraphicRoleOnSurface38, theme.onSurface38 ); setGraphicColor( GraphicRoleOnSurfaceVariant, theme.onSurfaceVariant ); diff --git a/designsystems/material3/QskMaterial3Skin.h b/designsystems/material3/QskMaterial3Skin.h index 309fd3cd..f50d9b73 100644 --- a/designsystems/material3/QskMaterial3Skin.h +++ b/designsystems/material3/QskMaterial3Skin.h @@ -114,7 +114,9 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Skin : public QskSkin public: enum GraphicRole { + GraphicRoleError, GraphicRoleOnError, + GraphicRoleOnErrorContainer, GraphicRoleOnPrimary, GraphicRoleOnPrimaryContainer, GraphicRoleOnSecondaryContainer, diff --git a/designsystems/material3/icons/qvg/text_field_cancel.qvg b/designsystems/material3/icons/qvg/text_field_cancel.qvg new file mode 100644 index 0000000000000000000000000000000000000000..7607d209e923154d72a71b8f1080c90c75b4ee6d GIT binary patch literal 1247 zcmchXy-Gtt5QX=r5Nt#Uf);`xDi|V&tvKB+Y)owP5Z3nAd5Bo2PAB*VR@SL)Y^>w# z?3|s+t!&~g?(TdubLQ-ZoAKj)7kbazcLi8E$~oom5biV+<9dxIFvR`!uX*Y>mUtV3 zSazpqpO+V%miNY!Xn)l$2G1);GxjcL%rsJ~yV{#x{JhUN&gh{VWSMD?V~MoKv5Y-( zqNh60V3t~)dC#r~gWFtyvOZ%etolNA-y`svA0-f9~Es zy>b-ktlo$7Vs)w^b<{9 literal 0 HcmV?d00001 diff --git a/designsystems/material3/icons/qvg/text_field_error.qvg b/designsystems/material3/icons/qvg/text_field_error.qvg new file mode 100644 index 0000000000000000000000000000000000000000..013508ad1e5d1797e959563eab46398739a4d7cf GIT binary patch literal 927 zcmWFx_I77L1N%Qf1Yk6f$IQ&c%n-m}50znL{Qn=O2_^>PqtnF7JD_U>@o8ZZ&{%em z+2SC=K?Ovj044{)*R66p0^cBsGQ-$NatJYm8BB0>`dqdJi?4G5`OGjq4kj>JpcqsS zk{FW%R1cCEGmH(B1Dgjm2WAFPj0vs|;dW*uJur*m_QC`}w1X0a=b#4{WrT}qA&IF$ z)nOCErWYZGutNmNJea*ubAd994lp~giDA==5EBBK3`(bg^iQV@G{_J@-!K6HY$HV? literal 0 HcmV?d00001 diff --git a/designsystems/material3/icons/qvg/text_field_search.qvg b/designsystems/material3/icons/qvg/text_field_search.qvg new file mode 100644 index 0000000000000000000000000000000000000000..b289e352b383cb75bca25bd9bc273db0172073b6 GIT binary patch literal 1107 zcmchWAx}d=49D+Uu>=DNQwSJj0fr!eU#NRaW_ZD((bEv*3}zn!^fa0;fktC67|dwz z_5bejFql#CUi)kN?_HCtucuY$m|Qdfe~p-l!&SKaY%(#fR%il4-2MMGFWp9q*D*+C zd!{|Gj~vZ+U+Ry!3?Ij{r?(A7QO+OC9m|f1LoTu015AZB7~>3u zMrK)q+B&~un&EfHE)x5_=g{r|Q*D1+x+z_kWXAB3(^O{VS-eN~zjagn`&0+LLr+?- w_ef@ho`Nxa%dlgl%)}ZPow(w!#`7vEGJ$06Q~&kpKVy literal 0 HcmV?d00001 diff --git a/designsystems/material3/icons/text_field_cancel.svg b/designsystems/material3/icons/text_field_cancel.svg new file mode 100644 index 00000000..3c8de038 --- /dev/null +++ b/designsystems/material3/icons/text_field_cancel.svg @@ -0,0 +1,4 @@ + + + + diff --git a/designsystems/material3/icons/text_field_error.svg b/designsystems/material3/icons/text_field_error.svg new file mode 100644 index 00000000..3b7b117b --- /dev/null +++ b/designsystems/material3/icons/text_field_error.svg @@ -0,0 +1,4 @@ + + + + diff --git a/designsystems/material3/icons/text_field_search.svg b/designsystems/material3/icons/text_field_search.svg new file mode 100644 index 00000000..d10c97b6 --- /dev/null +++ b/designsystems/material3/icons/text_field_search.svg @@ -0,0 +1,4 @@ + + + + diff --git a/examples/gallery/inputs/InputPage.cpp b/examples/gallery/inputs/InputPage.cpp index e733edce..5b18b49a 100644 --- a/examples/gallery/inputs/InputPage.cpp +++ b/examples/gallery/inputs/InputPage.cpp @@ -67,26 +67,51 @@ namespace InputBox( QQuickItem* parent = nullptr ) : QskLinearBox( Qt::Horizontal, parent ) { - setSpacing( 20 ); + setSpacing( 40 ); + setDefaultAlignment( Qt::AlignHCenter | Qt::AlignTop ); { - new QskTextInput( "Edit Me", this ); + auto spinBox = new QskSpinBox( 0.0, 100.0, 1.0, this ); + spinBox->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed ); + spinBox->setPageSize( 5 ); + spinBox->setValue( 35 ); } { - auto input = new QskTextInput( "Only Read Me", this ); + auto input = new QskTextInput( this ); + input->setLabelText( "filled" ); + input->setHintText( "hint text" ); + input->setSupportingText( "supporting text" ); + input->setMaxLength( 10 ); + } + + { + auto input = new QskTextInput( this ); + input->setLeadingIcon( {} ); + input->setLabelText( "no leading icon" ); + input->setHintText( "hint text" ); + input->setSupportingText( "supporting text" ); + } + auto input = new QskTextInput( this ); + input->setSkinStateFlag( QskTextInput::Error ); + input->setLabelText( "error" ); + input->setHintText( "hint text" ); + input->setSupportingText( "error text" ); + } + + { + auto input = new QskTextInput( this ); input->setReadOnly( true ); + input->setLabelText( "read only" ); input->setSizePolicy( Qt::Horizontal, QskSizePolicy::MinimumExpanding ); } { - auto input = new QskTextInput( "12345", this ); - input->setMaxLength( 5 ); - input->setEchoMode( QskTextInput::PasswordEchoOnEdit ); -#if 1 - input->setFixedWidth( 80 ); -#endif - } + auto input = new QskTextInput( this ); + input->setMaxLength( 15 ); + input->setLabelText( "password" ); + input->setEchoMode( QskTextInput::Password ); + input->setHintText( "better be strong" ); } }; } diff --git a/src/controls/QskTextInput.cpp b/src/controls/QskTextInput.cpp index 352d5ef9..3f33fdbc 100644 --- a/src/controls/QskTextInput.cpp +++ b/src/controls/QskTextInput.cpp @@ -4,6 +4,7 @@ *****************************************************************************/ #include "QskTextInput.h" +#include "QskEvent.h" #include "QskFontRole.h" #include "QskQuick.h" @@ -13,11 +14,20 @@ QSK_QT_PRIVATE_BEGIN QSK_QT_PRIVATE_END QSK_SUBCONTROL( QskTextInput, Panel ) +QSK_SUBCONTROL( QskTextInput, LeadingIcon ) +QSK_SUBCONTROL( QskTextInput, LabelText ) QSK_SUBCONTROL( QskTextInput, InputText ) +QSK_SUBCONTROL( QskTextInput, TrailingIconRipple ) +QSK_SUBCONTROL( QskTextInput, TrailingIcon ) +QSK_SUBCONTROL( QskTextInput, HintText ) +QSK_SUBCONTROL( QskTextInput, SupportingText ) +QSK_SUBCONTROL( QskTextInput, CharacterCount ) QSK_SYSTEM_STATE( QskTextInput, ReadOnly, QskAspect::FirstSystemState << 1 ) QSK_SYSTEM_STATE( QskTextInput, Editing, QskAspect::FirstSystemState << 2 ) QSK_SYSTEM_STATE( QskTextInput, Selected, QskAspect::FirstSystemState << 3 ) +QSK_SYSTEM_STATE( QskTextInput, Error, QskAspect::FirstSystemState << 4 ) +QSK_SYSTEM_STATE( QskTextInput, TextEmpty, QskAspect::LastUserState << 1 ) static inline void qskPropagateReadOnly( QskTextInput* input ) { @@ -284,7 +294,9 @@ class QskTextInput::PrivateData { public: TextInput* textInput; - QString description; // f.e. used as prompt in QskInputPanel + QString labelText; + QString hintText; + QString supportingText; unsigned int activationModes : 3; bool hasPanel : 1; @@ -318,12 +330,20 @@ QskTextInput::QskTextInput( QQuickItem* parent ) m_data->textInput->setAcceptedMouseButtons( Qt::NoButton ); initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Fixed ); + + setSkinStateFlag( TextEmpty ); + + connect( m_data->textInput, &QQuickTextInput::textChanged, this, [this]() + { + setSkinStateFlag( TextEmpty, m_data->textInput->text().isEmpty() ); + update(); // character count might have changed + } ); } QskTextInput::QskTextInput( const QString& text, QQuickItem* parent ) : QskTextInput( parent ) { - m_data->textInput->setText( text ); + setInputText( text ); } QskTextInput::~QskTextInput() @@ -420,7 +440,14 @@ void QskTextInput::keyReleaseEvent( QKeyEvent* event ) void QskTextInput::mousePressEvent( QMouseEvent* event ) { - m_data->textInput->handleEvent( event ); + if( !isReadOnly() && subControlContentsRect( TrailingIcon ).contains( event->position() ) ) + { + setInputText( {} ); + } + else + { + m_data->textInput->handleEvent( event ); + } if ( !isReadOnly() && !qGuiApp->styleHints()->setFocusOnTouchRelease() ) setEditing( true ); @@ -489,6 +516,36 @@ void QskTextInput::focusOutEvent( QFocusEvent* event ) Inherited::focusOutEvent( event ); } +void QskTextInput::hoverEnterEvent( QHoverEvent* event ) +{ + using A = QskAspect; + + setSkinHint( TrailingIconRipple | Hovered | A::Metric | A::Position, qskHoverPosition( event ) ); + update(); + + Inherited::hoverEnterEvent( event ); +} + +void QskTextInput::hoverMoveEvent( QHoverEvent* event ) +{ + using A = QskAspect; + + setSkinHint( TrailingIconRipple | Hovered | A::Metric | A::Position, qskHoverPosition( event ) ); + update(); + + Inherited::hoverMoveEvent( event ); +} + +void QskTextInput::hoverLeaveEvent( QHoverEvent* event ) +{ + using A = QskAspect; + + setSkinHint( TrailingIconRipple | Hovered | A::Metric | A::Position, QPointF() ); + update(); + + Inherited::hoverLeaveEvent( event ); +} + QSizeF QskTextInput::layoutSizeHint( Qt::SizeHint which, const QSizeF& ) const { if ( which != Qt::PreferredSize ) @@ -506,6 +563,12 @@ QSizeF QskTextInput::layoutSizeHint( Qt::SizeHint which, const QSizeF& ) const hint = hint.expandedTo( strutSizeHint( Panel ) ); } + if( !supportingText().isEmpty() || maxLength() != 32767 ) // magic number hardcoded in qquicktextinput.cpp + { + const auto margins = marginHint( SupportingText ); + hint.rheight() += margins.top() + effectiveFontHeight( SupportingText ) + margins.bottom(); + } + return hint; } @@ -534,18 +597,56 @@ void QskTextInput::setInputText( const QString& text ) m_data->textInput->setText( text ); } -void QskTextInput::setDescription( const QString& text ) +QString QskTextInput::labelText() const { - if ( m_data->description != text ) + return m_data->labelText; +} + +void QskTextInput::setLabelText( const QString& text ) +{ + if ( m_data->labelText != text ) { - m_data->description = text; - Q_EMIT descriptionChanged( text ); + m_data->labelText = text; + Q_EMIT labelTextChanged( text ); } } -QString QskTextInput::description() const +QskGraphic QskTextInput::leadingIcon() const { - return m_data->description; + return symbolHint( LeadingIcon ); +} + +void QskTextInput::setLeadingIcon( const QskGraphic& icon ) +{ + setSymbolHint( LeadingIcon, icon ); +} + +void QskTextInput::setHintText( const QString& text ) +{ + if ( m_data->hintText != text ) + { + m_data->hintText = text; + Q_EMIT hintTextChanged( text ); + } +} + +QString QskTextInput::hintText() const +{ + return m_data->hintText; +} + +void QskTextInput::setSupportingText( const QString& text ) +{ + if ( m_data->supportingText != text ) + { + m_data->supportingText = text; + Q_EMIT supportingTextChanged( text ); + } +} + +QString QskTextInput::supportingText() const +{ + return m_data->supportingText; } QskTextInput::ActivationModes QskTextInput::activationModes() const diff --git a/src/controls/QskTextInput.h b/src/controls/QskTextInput.h index de8aa429..f4ab77f4 100644 --- a/src/controls/QskTextInput.h +++ b/src/controls/QskTextInput.h @@ -7,6 +7,7 @@ #define QSK_TEXT_INPUT_H #include "QskControl.h" +#include "QskGraphic.h" #include "QskTextOptions.h" class QValidator; @@ -18,13 +19,18 @@ class QSK_EXPORT QskTextInput : public QskControl Q_PROPERTY( QString inputText READ inputText WRITE setInputText NOTIFY inputTextChanged USER true ) - Q_PROPERTY( QString description READ description - WRITE setDescription NOTIFY descriptionChanged ) + Q_PROPERTY( QString labelText READ labelText WRITE setLabelText NOTIFY labelTextChanged ) + + Q_PROPERTY( QString hintText READ hintText + WRITE setHintText NOTIFY hintTextChanged ) + + Q_PROPERTY( QString supportingText READ supportingText + WRITE setSupportingText NOTIFY supportingTextChanged ) Q_PROPERTY( QskFontRole fontRole READ fontRole WRITE setFontRole RESET resetFontRole NOTIFY fontRoleChanged ) - Q_PROPERTY( QFont font READ font ) + Q_PROPERTY( QFont font READ font CONSTANT ) Q_PROPERTY( Qt::Alignment alignment READ alignment WRITE setAlignment RESET resetAlignment NOTIFY alignmentChanged ) @@ -55,8 +61,11 @@ class QSK_EXPORT QskTextInput : public QskControl using Inherited = QskControl; public: - QSK_SUBCONTROLS( Panel, InputText ) - QSK_STATES( ReadOnly, Editing, Selected ) + QSK_SUBCONTROLS( Panel, LeadingIcon, LabelText, InputText, + TrailingIconRipple, TrailingIcon, HintText, SupportingText, + CharacterCount ) + + QSK_STATES( ReadOnly, Editing, Selected, Error, TextEmpty ) enum ActivationMode { @@ -84,7 +93,7 @@ class QSK_EXPORT QskTextInput : public QskControl Q_ENUM( EchoMode ) QskTextInput( QQuickItem* parent = nullptr ); - QskTextInput( const QString&, QQuickItem* parent = nullptr ); + QskTextInput( const QString&, QQuickItem* parent = nullptr ); // ### do we need this constructor? ~QskTextInput() override; @@ -92,8 +101,16 @@ class QSK_EXPORT QskTextInput : public QskControl QString inputText() const; - void setDescription( const QString& ); - QString description() const; + QString labelText() const; + + QskGraphic leadingIcon() const; + void setLeadingIcon( const QskGraphic& ); + + void setHintText( const QString& ); + QString hintText() const; + + void setSupportingText( const QString& ); + QString supportingText() const; void setPanel( bool ); bool hasPanel() const; @@ -164,6 +181,8 @@ class QSK_EXPORT QskTextInput : public QskControl public Q_SLOTS: void setInputText( const QString& ); + void setLabelText( const QString& ); + void setEditing( bool ); Q_SIGNALS: @@ -174,10 +193,13 @@ class QSK_EXPORT QskTextInput : public QskControl void panelChanged( bool ); void inputTextChanged( const QString& ); + void labelTextChanged( const QString& ); + void displayTextChanged( const QString& ); void textEdited( const QString& ); - void descriptionChanged( const QString& ); + void hintTextChanged( const QString& ); + void supportingTextChanged( const QString& ); void fontRoleChanged(); void alignmentChanged(); @@ -201,6 +223,10 @@ class QSK_EXPORT QskTextInput : public QskControl void focusInEvent( QFocusEvent* ) override; void focusOutEvent( QFocusEvent* ) override; + void hoverEnterEvent( QHoverEvent* ) override; + void hoverMoveEvent( QHoverEvent* ) override; + void hoverLeaveEvent( QHoverEvent* ) override; + void mousePressEvent( QMouseEvent* ) override; void mouseMoveEvent( QMouseEvent* ) override; void mouseReleaseEvent( QMouseEvent* ) override; diff --git a/src/controls/QskTextInputSkinlet.cpp b/src/controls/QskTextInputSkinlet.cpp index 027cce92..40756b58 100644 --- a/src/controls/QskTextInputSkinlet.cpp +++ b/src/controls/QskTextInputSkinlet.cpp @@ -6,10 +6,35 @@ #include "QskTextInputSkinlet.h" #include "QskTextInput.h" +#include "QskFunctions.h" + +#include + +using Q = QskTextInput; + +namespace +{ + QString maxLengthString( const QskTextInput* input ) + { + QString s = QString::number( input->inputText().length() ) + + " / " + QString::number( input->maxLength() ); + return s; + } +} + QskTextInputSkinlet::QskTextInputSkinlet( QskSkin* skin ) : Inherited( skin ) { - setNodeRoles( { PanelRole } ); + setNodeRoles( { + PanelRole, + LeadingIconRole, + LabelTextRole, + HintTextRole, + SupportingTextRole, + CharacterCountRole, + TrailingIconRippleRole, + TrailingIconRole, + } ); } QskTextInputSkinlet::~QskTextInputSkinlet() @@ -19,13 +44,158 @@ QskTextInputSkinlet::~QskTextInputSkinlet() QRectF QskTextInputSkinlet::subControlRect( const QskSkinnable* skinnable, const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const { - if ( subControl == QskTextInput::Panel ) + const auto input = static_cast< const Q* >( skinnable ); + + if ( subControl == Q::Panel ) { - return contentsRect; + auto rect = contentsRect; + + const auto h = input->strutSizeHint( subControl ).height(); + rect.setHeight( h ); + + return rect; } - else if ( subControl == QskTextInput::InputText ) + else if ( subControl == Q::LeadingIcon ) { - return skinnable->subControlContentsRect( contentsRect, QskTextInput::Panel ); + if( input->symbolHint( subControl ).isEmpty() ) + { + return {}; + } + else + { + const auto margins = input->marginHint( subControl ); + const auto panelRect = input->subControlRect( Q::Panel ); + auto rect = panelRect.marginsRemoved( margins ); + + const auto size = input->strutSizeHint( subControl ); + rect.setSize( size ); + rect.moveCenter( { rect.center().x(), panelRect.center().y() } ); + + return rect; + } + } + else if ( subControl == Q::LabelText ) + { + const auto inputRect = input->subControlRect( Q::InputText ); + + if( input->hasSkinState( Q::Focused ) || !input->inputText().isEmpty() ) + { + const auto margins = input->marginHint( subControl ); + auto rect = inputRect; + rect.setY( contentsRect.y() + margins.top() ); + const QFontMetricsF fm ( input->effectiveFont( subControl ) ); + rect.setHeight( fm.height() ); + return rect; + } + else + { + return inputRect; + } + } + else if ( subControl == Q::InputText ) + { + const auto margins = input->marginHint( subControl ); + + const auto leadingIconRect = input->subControlRect( Q::LeadingIcon ); + const auto panelRect = input->subControlRect( Q::Panel ); + auto rect = panelRect; + rect.setLeft( leadingIconRect.right() ); + rect.setRight( contentsRect.right() ); // ### space for trailing icon + rect = rect.marginsRemoved( margins ); + + return rect; + } + else if ( subControl == Q::HintText ) + { + if( input->hasSkinState( Q::Focused ) && input->inputText().isEmpty() ) // ### has TextEmpty state + { + return input->subControlRect( Q::InputText ); + } + else + { + return {}; + } + } + else if ( subControl == Q::SupportingText ) + { + if( input->supportingText().isEmpty() ) + { + return {}; + } + else + { + auto rect = contentsRect; + + const auto margins = input->marginHint( subControl ); + const auto h = margins.top() + input->effectiveFontHeight( subControl ) + margins.bottom(); + rect.setTop( rect.bottom() - h ); + + rect.setLeft( rect.left() + margins.left() ); + + return rect; + } + } + else if ( subControl == Q::CharacterCount ) + { + if( input->maxLength() == 32767 ) // magic number hardcoded in qquicktextinput.cpp + { + return {}; + } + else + { + auto rect = contentsRect; + + const auto margins = input->marginHint( subControl ); + const auto h = margins.top() + input->effectiveFontHeight( subControl ) + margins.bottom(); + rect.setTop( rect.bottom() - h ); + + const QFontMetricsF fm( input->effectiveFont( subControl ) ); + const auto w = qskHorizontalAdvance( fm, maxLengthString( input ) ); + rect.setRight( rect.right() - margins.right() ); + rect.setLeft( rect.right() - ( margins.left() + w + margins.right() ) ); + + return rect; + } + } + else if ( subControl == Q::TrailingIconRipple ) + { + const auto cursorPos = input->effectiveSkinHint( + Q::TrailingIconRipple | Q::Hovered | QskAspect::Metric | QskAspect::Position ).toPointF(); + const auto trailingIconRect = input->subControlRect( Q::TrailingIcon ); + + if( !cursorPos.isNull() && trailingIconRect.contains( cursorPos ) ) + { + const auto size = input->strutSizeHint( subControl ); + QRectF rect( { 0, 0 }, size ); + + rect.moveCenter( trailingIconRect.center() ); + + return rect; + } + else + { + return {}; + } + } + else if ( subControl == Q::TrailingIcon ) + { + if( input->symbolHint( subControl ).isEmpty() ) + { + return {}; + } + else + { + const auto margins = input->marginHint( subControl ); + const auto panelRect = input->subControlRect( Q::Panel ); + auto rect = panelRect.marginsRemoved( margins ); + + const auto size = input->strutSizeHint( subControl ); + rect.setHeight( size.height() ); + rect.moveCenter( { rect.center().x(), panelRect.center().y() } ); + rect.setLeft( rect.right() - size.width() ); + + return rect; + } } return Inherited::subControlRect( skinnable, contentsRect, subControl ); @@ -34,15 +204,51 @@ QRectF QskTextInputSkinlet::subControlRect( const QskSkinnable* skinnable, QSGNode* QskTextInputSkinlet::updateSubNode( const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const { + const auto input = static_cast< const Q* >( skinnable ); + switch ( nodeRole ) { case PanelRole: { - const auto input = static_cast< const QskTextInput* >( skinnable ); if ( !input->hasPanel() ) return nullptr; - return updateBoxNode( skinnable, node, QskTextInput::Panel ); + return updateBoxNode( skinnable, node, Q::Panel ); + } + + case LeadingIconRole: + { + return updateSymbolNode( skinnable, node, Q::LeadingIcon ); + } + + case LabelTextRole: + { + return updateTextNode( skinnable, node, input->labelText(), Q::LabelText ); + } + + case HintTextRole: + { + return updateTextNode( skinnable, node, input->hintText(), Q::HintText ); + } + + case SupportingTextRole: + { + return updateTextNode( skinnable, node, input->supportingText(), Q::SupportingText ); + } + + case CharacterCountRole: + { + return updateTextNode( skinnable, node, maxLengthString( input ), Q::CharacterCount ); + } + + case TrailingIconRippleRole: + { + return updateBoxNode( skinnable, node, Q::TrailingIconRipple ); + } + + case TrailingIconRole: + { + return updateSymbolNode( skinnable, node, Q::TrailingIcon ); } } diff --git a/src/controls/QskTextInputSkinlet.h b/src/controls/QskTextInputSkinlet.h index 946c6d32..904bb05c 100644 --- a/src/controls/QskTextInputSkinlet.h +++ b/src/controls/QskTextInputSkinlet.h @@ -18,6 +18,13 @@ class QSK_EXPORT QskTextInputSkinlet : public QskSkinlet enum NodeRole { PanelRole, + LeadingIconRole, + LabelTextRole, + HintTextRole, + SupportingTextRole, + TrailingIconRippleRole, + TrailingIconRole, + CharacterCountRole, RoleCount }; From 59a42395b2143e768d9283e6f28155d3c883d903 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Mon, 7 Oct 2024 14:46:55 +0200 Subject: [PATCH 04/11] text input: Add M3 outlined mode --- designsystems/material3/QskMaterial3Skin.cpp | 43 +++++++-- designsystems/material3/QskMaterial3Skin.h | 1 + examples/gallery/inputs/InputPage.cpp | 98 ++++++++++++-------- src/controls/QskTextInput.cpp | 42 +++++++++ src/controls/QskTextInput.h | 17 ++++ src/controls/QskTextInputSkinlet.cpp | 84 ++++++++++++++++- 6 files changed, 231 insertions(+), 54 deletions(-) diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index 076850b6..81ca3140 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -451,12 +451,16 @@ void Editor::setupTextLabel() void Editor::setupTextInput() { using Q = QskTextInput; + using M3 = QskMaterial3Skin; const QskStateCombination allStates( QskStateCombination::CombinationNoState, QskAspect::AllStates ); // Panel setStrutSize( Q::Panel, -1.0, 56_dp ); + + // Panel - Filled + setGradient( Q::Panel, m_pal.surfaceVariant ); setBoxShape( Q::Panel, m_pal.shapeExtraSmallTop ); @@ -484,6 +488,16 @@ void Editor::setupTextInput() setGradient( Q::Panel | Q::Disabled, disabledPanelColor, allStates ); setBoxBorderColors( Q::Panel | Q::Disabled, m_pal.onSurface38, allStates ); + // Panel - Outlined + + setGradient( Q::Panel | M3::Outlined, Qt::transparent ); + setBoxShape( Q::Panel | M3::Outlined, m_pal.shapeExtraSmall ); + setBoxBorderMetrics( Q::Panel | M3::Outlined, 1_dp ); + setBoxBorderColors( Q::Panel | M3::Outlined, m_pal.outline ); + + setBoxBorderMetrics( Q::Panel | M3::Outlined | Q::Focused, 2_dp, allStates ); + setBoxBorderColors( Q::Panel | M3::Outlined | Q::Focused, m_pal.primary, allStates ); + // LeadingIcon @@ -492,18 +506,19 @@ void Editor::setupTextInput() const auto leadingIcon = symbol( "text_field_search" ); setSymbol( Q::LeadingIcon, leadingIcon ); - setGraphicRole( Q::LeadingIcon, QskMaterial3Skin::GraphicRoleOnSurface ); - setGraphicRole( Q::LeadingIcon | Q::Error, QskMaterial3Skin::GraphicRoleOnSurfaceVariant, allStates ); + setGraphicRole( Q::LeadingIcon, M3::GraphicRoleOnSurface ); + setGraphicRole( Q::LeadingIcon | Q::Error, M3::GraphicRoleOnSurfaceVariant, allStates ); // LabelText - setAlignment( Q::LabelText, Qt::AlignLeft | Qt::AlignVCenter ); + setAlignment( Q::LabelText, Qt::AlignLeft | Qt::AlignTop ); const QskStateCombination textEmptyStates( QskStateCombination::CombinationNoState, - Q::Focused | Q::Hovered | Q::ReadOnly | Q::Disabled ); + Q::Hovered | Q::ReadOnly | Q::Disabled | Q::Error ); - setFontRole( Q::LabelText | Q::TextEmpty, BodyMedium, textEmptyStates ); + setAlignment( Q::LabelText | Q::TextEmpty, Qt::AlignLeft | Qt::AlignVCenter, textEmptyStates ); + setFontRole( Q::LabelText | Q::TextEmpty, BodyLarge, textEmptyStates ); setColor( Q::LabelText | Q::TextEmpty, m_pal.onSurfaceVariant, textEmptyStates ); const QskStateCombination editingHoveredFocused( QskStateCombination::CombinationNoState, @@ -516,38 +531,47 @@ void Editor::setupTextInput() setColor( Q::LabelText | Q::Error, m_pal.error, allStates ); setColor( Q::LabelText | Q::Error | Q::Hovered, m_pal.onErrorContainer, allStates ); + // LabelText - Outlined + + setMargin( Q::LabelText | M3::Outlined, { 4_dp, 0, 4_dp, 0 }, editingHoveredFocused ); + // InputText setMargin( Q::InputText, { 16_dp, 8_dp, 16_dp, 8_dp } ); setColor( Q::InputText, m_pal.onSurface ); - setFontRole( Q::InputText, BodyMedium ); + setFontRole( Q::InputText, BodyLarge ); setAlignment( Q::InputText, Qt::AlignLeft | Qt::AlignBottom ); setColor( Q::InputText | Q::Error, m_pal.onSurface, allStates ); // same as with Hovered and Focused setColor( Q::InputText | Q::Disabled, m_pal.onSurface38 ); + // InputText - Outlined + + setAlignment( Q::InputText | M3::Outlined, Qt::AlignLeft | Qt::AlignVCenter ); + // HintText setColor( Q::HintText, color( Q::InputText ) ); setFontRole( Q::HintText, fontRole( Q::InputText ) ); setAlignment( Q::HintText, alignment( Q::InputText ) ); + setAlignment( Q::HintText | M3::Outlined, alignment( Q::InputText | M3::Outlined ) ); // TrailingIcon setStrutSize( Q::TrailingIcon, { 24_dp, 24_dp } ); setMargin( Q::TrailingIcon, { 0, 0, 12_dp, 0 } ); - setGraphicRole( Q::TrailingIcon, QskMaterial3Skin::GraphicRoleOnSurface ); + setGraphicRole( Q::TrailingIcon, M3::GraphicRoleOnSurfaceVariant ); const auto trailingIcon = symbol( "text_field_cancel" ); setSymbol( Q::TrailingIcon, trailingIcon ); const auto errorIcon = symbol( "text_field_error" ); setSymbol( Q::TrailingIcon | Q::Error, errorIcon, allStates ); - setGraphicRole( Q::TrailingIcon | Q::Error, QskMaterial3Skin::GraphicRoleError, allStates ); - setGraphicRole( Q::TrailingIcon | Q::Error | Q::Hovered, QskMaterial3Skin::GraphicRoleOnErrorContainer, allStates ); + setGraphicRole( Q::TrailingIcon | Q::Error, M3::GraphicRoleError, allStates ); + setGraphicRole( Q::TrailingIcon | Q::Error | Q::Hovered, M3::GraphicRoleOnErrorContainer, allStates ); // TrailingIconRipple @@ -1654,6 +1678,7 @@ QskMaterial3Theme::QskMaterial3Theme( QskSkin::ColorScheme colorScheme, elevation2 = QskShadowMetrics( -2, 8, { 0, 2 } ); elevation3 = QskShadowMetrics( -1, 11, { 0, 2 } ); + shapeExtraSmall = QskBoxShapeMetrics( 4_dp, 4_dp, 4_dp, 4_dp ); shapeExtraSmallTop = QskBoxShapeMetrics( 4_dp, 4_dp, 0, 0 ); } diff --git a/designsystems/material3/QskMaterial3Skin.h b/designsystems/material3/QskMaterial3Skin.h index f50d9b73..f6e7dcaf 100644 --- a/designsystems/material3/QskMaterial3Skin.h +++ b/designsystems/material3/QskMaterial3Skin.h @@ -102,6 +102,7 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Theme qreal stateOpacity( int state ) const; + QskBoxShapeMetrics shapeExtraSmall; QskBoxShapeMetrics shapeExtraSmallTop; }; diff --git a/examples/gallery/inputs/InputPage.cpp b/examples/gallery/inputs/InputPage.cpp index 5b18b49a..d539433f 100644 --- a/examples/gallery/inputs/InputPage.cpp +++ b/examples/gallery/inputs/InputPage.cpp @@ -61,57 +61,70 @@ namespace } }; - class InputBox : public QskLinearBox + class TextInputBox : public QskLinearBox { public: - InputBox( QQuickItem* parent = nullptr ) + TextInputBox( QQuickItem* parent = nullptr ) : QskLinearBox( Qt::Horizontal, parent ) { - setSpacing( 40 ); + setSpacing( 25 ); setDefaultAlignment( Qt::AlignHCenter | Qt::AlignTop ); - { - auto spinBox = new QskSpinBox( 0.0, 100.0, 1.0, this ); - spinBox->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed ); - spinBox->setPageSize( 5 ); - spinBox->setValue( 35 ); } + for( const auto& emphasis : { QskTextInput::NoEmphasis, QskTextInput::LowEmphasis } ) { - auto input = new QskTextInput( this ); - input->setLabelText( "filled" ); - input->setHintText( "hint text" ); - input->setSupportingText( "supporting text" ); - input->setMaxLength( 10 ); - } + { + auto input = new QskTextInput( this ); + input->setEmphasis( emphasis ); + const QString text = ( emphasis == QskTextInput::NoEmphasis ) ? "filled" : "outlined"; + input->setLabelText( text ); + input->setHintText( "hint text" ); + input->setSupportingText( "supporting text" ); + input->setMaxLength( 10 ); + } - { - auto input = new QskTextInput( this ); - input->setLeadingIcon( {} ); - input->setLabelText( "no leading icon" ); - input->setHintText( "hint text" ); - input->setSupportingText( "supporting text" ); - } - auto input = new QskTextInput( this ); - input->setSkinStateFlag( QskTextInput::Error ); - input->setLabelText( "error" ); - input->setHintText( "hint text" ); - input->setSupportingText( "error text" ); - } + { + auto input = new QskTextInput( this ); + input->setEmphasis( emphasis ); + input->setLeadingIcon( {} ); + input->setLabelText( "no leading icon" ); + input->setHintText( "hint text" ); + input->setSupportingText( "supporting text" ); + } + { + auto input = new QskTextInput( this ); + input->setEmphasis( emphasis ); + input->setLeadingIcon( {} ); + input->setLabelText( "no hint text" ); + } - { - auto input = new QskTextInput( this ); - input->setReadOnly( true ); - input->setLabelText( "read only" ); - input->setSizePolicy( Qt::Horizontal, QskSizePolicy::MinimumExpanding ); - } + { + auto input = new QskTextInput( this ); + input->setEmphasis( emphasis ); + input->setSkinStateFlag( QskTextInput::Error ); + input->setLabelText( "error" ); + input->setHintText( "hint text" ); + input->setSupportingText( "error text" ); + } - { - auto input = new QskTextInput( this ); - input->setMaxLength( 15 ); - input->setLabelText( "password" ); - input->setEchoMode( QskTextInput::Password ); - input->setHintText( "better be strong" ); + { + auto input = new QskTextInput( this ); + input->setEmphasis( emphasis ); + input->setReadOnly( true ); + input->setLabelText( "read only" ); + input->setSizePolicy( Qt::Horizontal, QskSizePolicy::MinimumExpanding ); + } + + { + auto input = new QskTextInput( this ); + input->setEmphasis( emphasis ); + input->setMaxLength( 15 ); + input->setLabelText( "password" ); + input->setEchoMode( QskTextInput::Password ); + input->setHintText( "better be strong" ); + } + } } }; } @@ -136,12 +149,17 @@ InputPage::InputPage( QQuickItem* parent ) auto spinBox = new QskSpinBox( 0.0, 100.0, 1.0 ); spinBox->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed ); - auto inputBox = new InputBox(); + auto textInputBox = new TextInputBox(); inputBox->setSizePolicy( Qt::Vertical, QskSizePolicy::Fixed ); auto vBox = new QskLinearBox( Qt::Vertical ); vBox->setSpacing( 30 ); vBox->setExtraSpacingAt( Qt::RightEdge | Qt::BottomEdge ); + + auto spinBox = new QskSpinBox( 0.0, 100.0, 1.0 ); + spinBox->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed ); + spinBox->setPageSize( 5 ); + spinBox->setValue( 35 ); vBox->addItem( sliders[0].continous ); vBox->addItem( sliders[0].discrete ); diff --git a/src/controls/QskTextInput.cpp b/src/controls/QskTextInput.cpp index 3f33fdbc..da0e72ad 100644 --- a/src/controls/QskTextInput.cpp +++ b/src/controls/QskTextInput.cpp @@ -293,6 +293,11 @@ namespace class QskTextInput::PrivateData { public: + PrivateData() + : emphasis( NoEmphasis ) + { + } + TextInput* textInput; QString labelText; QString hintText; @@ -300,6 +305,7 @@ class QskTextInput::PrivateData unsigned int activationModes : 3; bool hasPanel : 1; + int emphasis : 4; }; QskTextInput::QskTextInput( QQuickItem* parent ) @@ -563,6 +569,12 @@ QSizeF QskTextInput::layoutSizeHint( Qt::SizeHint which, const QSizeF& ) const hint = hint.expandedTo( strutSizeHint( Panel ) ); } + if( emphasis() == LowEmphasis ) + { + const auto fontHeight = effectiveFontHeight( LabelText | TextEmpty ); + hint.rheight() += fontHeight / 2; + } + if( !supportingText().isEmpty() || maxLength() != 32767 ) // magic number hardcoded in qquicktextinput.cpp { const auto margins = marginHint( SupportingText ); @@ -587,6 +599,24 @@ void QskTextInput::updateNode( QSGNode* node ) Inherited::updateNode( node ); } +void QskTextInput::setEmphasis( Emphasis emphasis ) +{ + if ( emphasis != m_data->emphasis ) + { + m_data->emphasis = emphasis; + + resetImplicitSize(); + update(); + + Q_EMIT emphasisChanged( emphasis ); + } +} + +QskTextInput::Emphasis QskTextInput::emphasis() const +{ + return static_cast< Emphasis >( m_data->emphasis ); +} + QString QskTextInput::inputText() const { return m_data->textInput->text(); @@ -798,6 +828,18 @@ void QskTextInput::ensureVisible( int position ) m_data->textInput->ensureVisible( position ); } +QskAspect::Variation QskTextInput::effectiveVariation() const +{ + switch( m_data->emphasis ) + { + case LowEmphasis: + return QskAspect::Small; + + default: + return QskAspect::NoVariation; + } +} + int QskTextInput::cursorPosition() const { return m_data->textInput->cursorPosition(); diff --git a/src/controls/QskTextInput.h b/src/controls/QskTextInput.h index f4ab77f4..d03516bb 100644 --- a/src/controls/QskTextInput.h +++ b/src/controls/QskTextInput.h @@ -58,6 +58,9 @@ class QSK_EXPORT QskTextInput : public QskControl Q_PROPERTY( bool panel READ hasPanel WRITE setPanel NOTIFY panelChanged ) + Q_PROPERTY( Emphasis emphasis READ emphasis + WRITE setEmphasis NOTIFY emphasisChanged ) + using Inherited = QskControl; public: @@ -67,6 +70,13 @@ class QSK_EXPORT QskTextInput : public QskControl QSK_STATES( ReadOnly, Editing, Selected, Error, TextEmpty ) + enum Emphasis + { + LowEmphasis = -1, + NoEmphasis = 0, + }; + Q_ENUM( Emphasis ) + enum ActivationMode { NoActivation, @@ -99,6 +109,9 @@ class QSK_EXPORT QskTextInput : public QskControl void setupFrom( const QQuickItem* ); + void setEmphasis( Emphasis ); + Emphasis emphasis() const; + QString inputText() const; QString labelText() const; @@ -179,6 +192,8 @@ class QSK_EXPORT QskTextInput : public QskControl void ensureVisible( int position ); + QskAspect::Variation effectiveVariation() const override; + public Q_SLOTS: void setInputText( const QString& ); void setLabelText( const QString& ); @@ -186,6 +201,8 @@ class QSK_EXPORT QskTextInput : public QskControl void setEditing( bool ); Q_SIGNALS: + void emphasisChanged( Emphasis ); + void editingChanged( bool ); void activationModesChanged(); diff --git a/src/controls/QskTextInputSkinlet.cpp b/src/controls/QskTextInputSkinlet.cpp index 40756b58..902c2b1b 100644 --- a/src/controls/QskTextInputSkinlet.cpp +++ b/src/controls/QskTextInputSkinlet.cpp @@ -6,6 +6,9 @@ #include "QskTextInputSkinlet.h" #include "QskTextInput.h" +#include "QskBoxBorderColors.h" +#include "QskBoxBorderMetrics.h" +#include "QskBoxShapeMetrics.h" #include "QskFunctions.h" #include @@ -20,6 +23,39 @@ namespace + " / " + QString::number( input->maxLength() ); return s; } + + // We need to "cut a hole" in the upper gradient for the label text: + QskBoxBorderColors outlineColors( const QskTextInput* input ) + { + auto borderColors = input->boxBorderColorsHint( Q::Panel ); + auto topGradient = borderColors.gradientAt( Qt::TopEdge ); + + const auto panelRect = input->subControlRect( Q::Panel ); + + const auto margins = input->marginHint( Q::LabelText ); + const auto iconMargins = input->marginHint( Q::LeadingIcon ); + + const auto x1 = iconMargins.left() - margins.left(); + const auto r1 = x1 / panelRect.width(); + + const auto w = qskHorizontalAdvance( input->effectiveFont( Q::LabelText ), input->labelText() ); + + const auto x2 = x1 + w + margins.right(); + const auto r2 = x2 / panelRect.width(); + + topGradient.setStops( { + { 0.0, topGradient.startColor() }, + { r1, topGradient.startColor() }, + { r1, Qt::transparent }, + { r2, Qt::transparent }, + { r2, topGradient.startColor() }, + { 1.0, topGradient.startColor() } + } ); + + borderColors.setGradientAt( Qt::TopEdge, topGradient ); + + return borderColors; + } } QskTextInputSkinlet::QskTextInputSkinlet( QskSkin* skin ) @@ -50,6 +86,13 @@ QRectF QskTextInputSkinlet::subControlRect( const QskSkinnable* skinnable, { auto rect = contentsRect; + if( input->emphasis() == Q::LowEmphasis ) + { + const auto fontHeight = input->effectiveFontHeight( Q::LabelText | Q::Focused ); + rect.setY( fontHeight / 2 ); + } + + const auto h = input->strutSizeHint( subControl ).height(); rect.setHeight( h ); @@ -78,13 +121,27 @@ QRectF QskTextInputSkinlet::subControlRect( const QskSkinnable* skinnable, { const auto inputRect = input->subControlRect( Q::InputText ); - if( input->hasSkinState( Q::Focused ) || !input->inputText().isEmpty() ) + if( !input->inputText().isEmpty() + || input->hasSkinState( Q::Focused ) + || input->hasSkinState( Q::Editing ) ) { const auto margins = input->marginHint( subControl ); auto rect = inputRect; - rect.setY( contentsRect.y() + margins.top() ); - const QFontMetricsF fm ( input->effectiveFont( subControl ) ); + const QFontMetricsF fm( input->effectiveFont( subControl ) ); + + if( input->emphasis() == Q::LowEmphasis ) + { + const auto iconMargins = input->marginHint( Q::LeadingIcon ); + rect.setX( iconMargins.left() ); + rect.setY( 0 ); + } + else + { + rect.setY( contentsRect.y() + margins.top() ); + } + rect.setHeight( fm.height() ); + return rect; } else @@ -107,7 +164,8 @@ QRectF QskTextInputSkinlet::subControlRect( const QskSkinnable* skinnable, } else if ( subControl == Q::HintText ) { - if( input->hasSkinState( Q::Focused ) && input->inputText().isEmpty() ) // ### has TextEmpty state + if( input->hasSkinState( Q::TextEmpty ) + && ( input->hasSkinState( Q::Focused ) || input->hasSkinState( Q::Editing ) ) ) { return input->subControlRect( Q::InputText ); } @@ -213,7 +271,23 @@ QSGNode* QskTextInputSkinlet::updateSubNode( if ( !input->hasPanel() ) return nullptr; - return updateBoxNode( skinnable, node, Q::Panel ); + if( input->emphasis() == Q::LowEmphasis + && ( !input->hasSkinState( Q::TextEmpty ) + || input->hasSkinState( Q::Focused ) + || input->hasSkinState( Q::Editing ) ) ) + { + const auto shape = skinnable->boxShapeHint( Q::Panel ); + const auto borderMetrics = skinnable->boxBorderMetricsHint( Q::Panel ); + const auto borderColors = outlineColors( input ); + const auto gradient = input->gradientHint( Q::Panel ); + + return updateBoxNode( skinnable, node, input->subControlRect( Q::Panel ), + shape, borderMetrics, borderColors, gradient ); + } + else + { + return updateBoxNode( skinnable, node, Q::Panel ); + } } case LeadingIconRole: From ab37df2c1716f51245666ea08e388c8843ce3dee Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Mon, 14 Oct 2024 13:24:03 +0200 Subject: [PATCH 05/11] input text: Fix Focused / Editing states and simplify styling --- designsystems/material3/QskMaterial3Skin.cpp | 27 +++++++++----------- src/controls/QskTextInput.cpp | 8 +++--- src/controls/QskTextInput.h | 2 +- src/controls/QskTextInputSkinlet.cpp | 17 +++++++++--- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index 81ca3140..e7d88b8d 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -463,6 +463,8 @@ void Editor::setupTextInput() setGradient( Q::Panel, m_pal.surfaceVariant ); + setColor( Q::Panel | Q::Selected, m_pal.primary12 ); + setBoxShape( Q::Panel, m_pal.shapeExtraSmallTop ); setBoxBorderMetrics( Q::Panel, { 0, 0, 0, 1_dp } ); @@ -512,28 +514,23 @@ void Editor::setupTextInput() // LabelText - setAlignment( Q::LabelText, Qt::AlignLeft | Qt::AlignTop ); + setAlignment( Q::LabelText, Qt::AlignLeft | Qt::AlignVCenter ); + setFontRole( Q::LabelText, BodyLarge ); + setColor( Q::LabelText, m_pal.onSurfaceVariant ); - const QskStateCombination textEmptyStates( QskStateCombination::CombinationNoState, - Q::Hovered | Q::ReadOnly | Q::Disabled | Q::Error ); - - setAlignment( Q::LabelText | Q::TextEmpty, Qt::AlignLeft | Qt::AlignVCenter, textEmptyStates ); - setFontRole( Q::LabelText | Q::TextEmpty, BodyLarge, textEmptyStates ); - setColor( Q::LabelText | Q::TextEmpty, m_pal.onSurfaceVariant, textEmptyStates ); - - const QskStateCombination editingHoveredFocused( QskStateCombination::CombinationNoState, - Q::Editing | Q::Hovered | Q::Focused ); - - setMargin( Q::LabelText, { 16_dp, 8_dp, 16_dp, 16_dp }, editingHoveredFocused ); - setColor( Q::LabelText, m_pal.primary, editingHoveredFocused ); - setFontRole( Q::LabelText, BodySmall, editingHoveredFocused ); + for( const auto s : { Q::Focused, Q::Editing, Q::TextPopulated } ) + { + setFontRole( Q::LabelText | s, BodySmall, allStates ); + setMargin( Q::LabelText | s, { 16_dp, 4_dp, 16_dp, 16_dp }, allStates ); + setColor( Q::LabelText | s, m_pal.primary, allStates ); + } setColor( Q::LabelText | Q::Error, m_pal.error, allStates ); setColor( Q::LabelText | Q::Error | Q::Hovered, m_pal.onErrorContainer, allStates ); // LabelText - Outlined - setMargin( Q::LabelText | M3::Outlined, { 4_dp, 0, 4_dp, 0 }, editingHoveredFocused ); + setMargin( Q::LabelText | M3::Outlined, { 4_dp, 0, 4_dp, 0 }, allStates ); // InputText diff --git a/src/controls/QskTextInput.cpp b/src/controls/QskTextInput.cpp index da0e72ad..3971545e 100644 --- a/src/controls/QskTextInput.cpp +++ b/src/controls/QskTextInput.cpp @@ -27,7 +27,7 @@ QSK_SYSTEM_STATE( QskTextInput, ReadOnly, QskAspect::FirstSystemState << 1 ) QSK_SYSTEM_STATE( QskTextInput, Editing, QskAspect::FirstSystemState << 2 ) QSK_SYSTEM_STATE( QskTextInput, Selected, QskAspect::FirstSystemState << 3 ) QSK_SYSTEM_STATE( QskTextInput, Error, QskAspect::FirstSystemState << 4 ) -QSK_SYSTEM_STATE( QskTextInput, TextEmpty, QskAspect::LastUserState << 1 ) +QSK_SYSTEM_STATE( QskTextInput, TextPopulated, QskAspect::LastUserState << 1 ) static inline void qskPropagateReadOnly( QskTextInput* input ) { @@ -337,11 +337,9 @@ QskTextInput::QskTextInput( QQuickItem* parent ) initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Fixed ); - setSkinStateFlag( TextEmpty ); - connect( m_data->textInput, &QQuickTextInput::textChanged, this, [this]() { - setSkinStateFlag( TextEmpty, m_data->textInput->text().isEmpty() ); + setSkinStateFlag( TextPopulated, !m_data->textInput->text().isEmpty() ); update(); // character count might have changed } ); } @@ -571,7 +569,7 @@ QSizeF QskTextInput::layoutSizeHint( Qt::SizeHint which, const QSizeF& ) const if( emphasis() == LowEmphasis ) { - const auto fontHeight = effectiveFontHeight( LabelText | TextEmpty ); + const auto fontHeight = effectiveFontHeight( LabelText | Focused ); hint.rheight() += fontHeight / 2; } diff --git a/src/controls/QskTextInput.h b/src/controls/QskTextInput.h index d03516bb..41e0fe05 100644 --- a/src/controls/QskTextInput.h +++ b/src/controls/QskTextInput.h @@ -68,7 +68,7 @@ class QSK_EXPORT QskTextInput : public QskControl TrailingIconRipple, TrailingIcon, HintText, SupportingText, CharacterCount ) - QSK_STATES( ReadOnly, Editing, Selected, Error, TextEmpty ) + QSK_STATES( ReadOnly, Editing, Selected, Error, TextPopulated ) enum Emphasis { diff --git a/src/controls/QskTextInputSkinlet.cpp b/src/controls/QskTextInputSkinlet.cpp index 902c2b1b..2e48074b 100644 --- a/src/controls/QskTextInputSkinlet.cpp +++ b/src/controls/QskTextInputSkinlet.cpp @@ -157,14 +157,25 @@ QRectF QskTextInputSkinlet::subControlRect( const QskSkinnable* skinnable, const auto panelRect = input->subControlRect( Q::Panel ); auto rect = panelRect; rect.setLeft( leadingIconRect.right() ); - rect.setRight( contentsRect.right() ); // ### space for trailing icon + + const auto trailingIconRect = input->subControlRect( Q::TrailingIcon ); + + if( trailingIconRect.isValid() ) + { + rect.setRight( trailingIconRect.left() ); + } + else + { + rect.setRight( contentsRect.right() ); + } + rect = rect.marginsRemoved( margins ); return rect; } else if ( subControl == Q::HintText ) { - if( input->hasSkinState( Q::TextEmpty ) + if( !input->hasSkinState( Q::TextPopulated ) && ( input->hasSkinState( Q::Focused ) || input->hasSkinState( Q::Editing ) ) ) { return input->subControlRect( Q::InputText ); @@ -272,7 +283,7 @@ QSGNode* QskTextInputSkinlet::updateSubNode( return nullptr; if( input->emphasis() == Q::LowEmphasis - && ( !input->hasSkinState( Q::TextEmpty ) + && ( input->hasSkinState( Q::TextPopulated ) || input->hasSkinState( Q::Focused ) || input->hasSkinState( Q::Editing ) ) ) { From 1d94427407a8e55005436a4a0215c337d3c8a437 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Mon, 14 Oct 2024 14:05:13 +0200 Subject: [PATCH 06/11] input text: Add disabled hints --- designsystems/material3/QskMaterial3Skin.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index e7d88b8d..0f63c219 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -511,6 +511,7 @@ void Editor::setupTextInput() setGraphicRole( Q::LeadingIcon, M3::GraphicRoleOnSurface ); setGraphicRole( Q::LeadingIcon | Q::Error, M3::GraphicRoleOnSurfaceVariant, allStates ); + setGraphicRole( Q::LeadingIcon | Q::Disabled, M3::GraphicRoleOnSurface38, allStates ); // LabelText @@ -528,6 +529,8 @@ void Editor::setupTextInput() setColor( Q::LabelText | Q::Error, m_pal.error, allStates ); setColor( Q::LabelText | Q::Error | Q::Hovered, m_pal.onErrorContainer, allStates ); + setColor( Q::LabelText | Q::Disabled, m_pal.onSurface38, allStates ); + // LabelText - Outlined setMargin( Q::LabelText | M3::Outlined, { 4_dp, 0, 4_dp, 0 }, allStates ); @@ -542,7 +545,7 @@ void Editor::setupTextInput() setColor( Q::InputText | Q::Error, m_pal.onSurface, allStates ); // same as with Hovered and Focused - setColor( Q::InputText | Q::Disabled, m_pal.onSurface38 ); + setColor( Q::InputText | Q::Disabled, m_pal.onSurface38, allStates ); // InputText - Outlined @@ -570,6 +573,7 @@ void Editor::setupTextInput() setGraphicRole( Q::TrailingIcon | Q::Error, M3::GraphicRoleError, allStates ); setGraphicRole( Q::TrailingIcon | Q::Error | Q::Hovered, M3::GraphicRoleOnErrorContainer, allStates ); + setGraphicRole( Q::TrailingIcon | Q::Disabled, M3::GraphicRoleOnSurface38, allStates ); // TrailingIconRipple @@ -586,6 +590,7 @@ void Editor::setupTextInput() setFontRole( Q::SupportingText, BodySmall ); setAlignment( Q::SupportingText, Qt::AlignLeft | Qt::AlignVCenter ); + setColor( Q::SupportingText | Q::Disabled, m_pal.onSurface38, allStates ); // CharacterCount @@ -593,6 +598,7 @@ void Editor::setupTextInput() setColor( Q::CharacterCount, color( Q::SupportingText ) ); setFontRole( Q::CharacterCount, fontRole( Q::SupportingText ) ); setAlignment( Q::CharacterCount, Qt::AlignRight | Qt::AlignVCenter ); + setColor( Q::CharacterCount | Q::Disabled, color( Q::SupportingText | Q::Disabled ) ); } void Editor::setupProgressBar() @@ -1664,7 +1670,7 @@ QskMaterial3Theme::QskMaterial3Theme( QskSkin::ColorScheme colorScheme, onSurface8 = QskRgb::toTransparentF( onSurface, 0.08 ); onSurface12 = QskRgb::toTransparentF( onSurface, 0.12 ); - onSurface38 = QskRgb::toTransparentF( onSurface, 0.38 ); + onSurface38 = QskRgb::toTransparentF( onSurface, 0.38 ); // ### rename to onSurfaceDisabled surfaceVariant12 = QskRgb::toTransparentF( surfaceVariant, 0.12 ); From a0642cfde994097456c1aba1aad9c90a3b823447 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Mon, 14 Oct 2024 16:07:35 +0200 Subject: [PATCH 07/11] input text: Add skinlets for different design systems --- designsystems/fluent2/CMakeLists.txt | 1 + designsystems/fluent2/QskFluent2Skin.cpp | 22 +- designsystems/fluent2/QskFluent2Skin.h | 1 + .../fluent2/QskFluent2TextInputSkinlet.cpp | 75 ++++ .../fluent2/QskFluent2TextInputSkinlet.h | 28 ++ designsystems/fusion/QskFusionSkin.cpp | 2 +- designsystems/material3/CMakeLists.txt | 1 + designsystems/material3/QskMaterial3Skin.cpp | 8 + designsystems/material3/QskMaterial3Skin.h | 1 + .../QskMaterial3TextInputSkinlet.cpp | 332 ++++++++++++++++++ .../material3/QskMaterial3TextInputSkinlet.h | 41 +++ examples/gallery/inputs/InputPage.cpp | 2 +- src/controls/QskTextInput.cpp | 16 +- src/controls/QskTextInputSkinlet.cpp | 254 +------------- src/controls/QskTextInputSkinlet.h | 8 +- 15 files changed, 522 insertions(+), 270 deletions(-) create mode 100644 designsystems/fluent2/QskFluent2TextInputSkinlet.cpp create mode 100644 designsystems/fluent2/QskFluent2TextInputSkinlet.h create mode 100644 designsystems/material3/QskMaterial3TextInputSkinlet.cpp create mode 100644 designsystems/material3/QskMaterial3TextInputSkinlet.h diff --git a/designsystems/fluent2/CMakeLists.txt b/designsystems/fluent2/CMakeLists.txt index 4fb73177..ce0d222c 100644 --- a/designsystems/fluent2/CMakeLists.txt +++ b/designsystems/fluent2/CMakeLists.txt @@ -8,6 +8,7 @@ set(SOURCES QskFluent2Theme.h QskFluent2Theme.cpp QskFluent2Skin.h QskFluent2Skin.cpp QskFluent2SkinFactory.h QskFluent2SkinFactory.cpp + QskFluent2TextInputSkinlet.h QskFluent2TextInputSkinlet.cpp ) qt_add_resources(SOURCES QskFluent2Icons.qrc) diff --git a/designsystems/fluent2/QskFluent2Skin.cpp b/designsystems/fluent2/QskFluent2Skin.cpp index caf41258..9f7dbc1f 100644 --- a/designsystems/fluent2/QskFluent2Skin.cpp +++ b/designsystems/fluent2/QskFluent2Skin.cpp @@ -48,6 +48,8 @@ #include "QskFluent2Skin.h" #include "QskFluent2Theme.h" +#include "QskFluent2TextInputSkinlet.h" + #include #include @@ -1773,8 +1775,14 @@ void Editor::setupTextInputMetrics() setBoxShape( Q::Panel, 3_px ); + setStrutSize( Q::LabelText, { -1, 30_px } ); + setFontRole( Q::LabelText, Fluent2::Body ); + setAlignment( Q::InputText, Qt::AlignLeft | Qt::AlignVCenter ); setFontRole( Q::InputText, Fluent2::Body ); + + setAlignment( Q::HintText, alignment( Q::InputText ) ); + setFontRole( Q::HintText, fontRole( Q::InputText ) ); } void Editor::setupTextInputColors( @@ -1786,7 +1794,9 @@ void Editor::setupTextInputColors( const auto& pal = theme.palette; setColor( Q::Panel | Q::Selected, pal.fillColor.accent.selectedTextBackground ); + setColor( Q::LabelText, pal.fillColor.text.primary ); setColor( Q::InputText | Q::Selected, pal.fillColor.textOnAccent.selectedText ); + setColor( Q::HintText, pal.fillColor.text.secondary ); for( const auto state : { A::NoState, Q::Hovered, Q::Focused, Q::Editing, Q::Disabled } ) { @@ -1797,21 +1807,21 @@ void Editor::setupTextInputColors( panelColor = pal.fillColor.control.defaultColor; borderColor1 = pal.elevation.textControl.border[0]; borderColor2 = pal.elevation.textControl.border[1]; - textColor = pal.fillColor.text.secondary; + textColor = pal.fillColor.text.primary; } else if ( state == Q::Hovered ) { panelColor = pal.fillColor.control.secondary; borderColor1 = pal.elevation.textControl.border[0]; borderColor2 = pal.elevation.textControl.border[1]; - textColor = pal.fillColor.text.secondary; + textColor = pal.fillColor.text.primary; } else if ( ( state == Q::Focused ) || ( state == Q::Editing ) ) { panelColor = pal.fillColor.control.inputActive; borderColor1 = pal.elevation.textControl.border[0]; borderColor2 = pal.fillColor.accent.defaultColor; - textColor = pal.fillColor.text.secondary; + textColor = pal.fillColor.text.primary; } else if ( state == Q::Disabled ) { @@ -2016,6 +2026,7 @@ void Editor::setupVirtualKeyboardColors( QskFluent2Skin::QskFluent2Skin( QObject* parent ) : Inherited( parent ) { + setupSkinlets(); setupFonts(); Editor editor( &hintTable() ); @@ -2103,6 +2114,11 @@ static inline QFont createFont( qreal size, int lineHeight, QFont::Weight weight return font; } +void QskFluent2Skin::setupSkinlets() +{ + declareSkinlet< QskTextInput, QskFluent2TextInputSkinlet >(); +} + void QskFluent2Skin::setupFonts() { // see: https://fluent2.microsoft.design/typography ( Windows ) diff --git a/designsystems/fluent2/QskFluent2Skin.h b/designsystems/fluent2/QskFluent2Skin.h index 5a72c481..2d919a84 100644 --- a/designsystems/fluent2/QskFluent2Skin.h +++ b/designsystems/fluent2/QskFluent2Skin.h @@ -40,6 +40,7 @@ class QSK_FLUENT2_EXPORT QskFluent2Skin : public QskSkin private: void addTheme( QskAspect::Section, const QskFluent2Theme& ); + void setupSkinlets(); void setupFonts(); void setupGraphicFilters( const QskFluent2Theme& ); void setGraphicColor( GraphicRole, QRgb ); diff --git a/designsystems/fluent2/QskFluent2TextInputSkinlet.cpp b/designsystems/fluent2/QskFluent2TextInputSkinlet.cpp new file mode 100644 index 00000000..656028c8 --- /dev/null +++ b/designsystems/fluent2/QskFluent2TextInputSkinlet.cpp @@ -0,0 +1,75 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskFluent2TextInputSkinlet.h" +#include "QskTextInput.h" + +using Q = QskTextInput; + +QskFluent2TextInputSkinlet::QskFluent2TextInputSkinlet( QskSkin* skin ) + : Inherited( skin ) +{ +} + +QskFluent2TextInputSkinlet::~QskFluent2TextInputSkinlet() +{ +} + +QRectF QskFluent2TextInputSkinlet::subControlRect( const QskSkinnable* skinnable, + const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const +{ + const auto input = static_cast< const Q* >( skinnable ); + + if ( subControl == Q::Panel ) + { + auto rect = contentsRect; + + const auto h = input->strutSizeHint( subControl ).height(); + rect.setY( rect.bottom() - h ); + + return rect; + } + else if ( subControl == Q::LabelText ) + { + auto rect = contentsRect; + + const auto h = input->strutSizeHint( subControl ).height(); + rect.setHeight( h ); + + return rect; + } + else if ( subControl == Q::HintText ) + { + if( input->hasSkinState( Q::TextPopulated ) ) + { + return {}; + } + else + { + return input->subControlRect( Q::InputText ); + } + } + + return Inherited::subControlRect( skinnable, contentsRect, subControl ); +} + +QSizeF QskFluent2TextInputSkinlet::adjustSizeHint( const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& oldHint ) const +{ + if ( which != Qt::PreferredSize ) + return QSizeF(); + + const auto input = static_cast< const Q* >( skinnable ); + + const auto labelHeight = input->labelText().isEmpty() ? 0 : input->strutSizeHint( Q::LabelText ).height(); + const auto panelHeight = input->strutSizeHint( Q::Panel ).height(); + + const auto h = labelHeight + panelHeight; + + QSizeF hint( oldHint.width(), h ); + + return hint; +} + +#include "moc_QskFluent2TextInputSkinlet.cpp" diff --git a/designsystems/fluent2/QskFluent2TextInputSkinlet.h b/designsystems/fluent2/QskFluent2TextInputSkinlet.h new file mode 100644 index 00000000..79e9cd26 --- /dev/null +++ b/designsystems/fluent2/QskFluent2TextInputSkinlet.h @@ -0,0 +1,28 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_MATERIAL3_INPUT_SKINLET_H +#define QSK_MATERIAL3_INPUT_SKINLET_H + +#include "QskTextInputSkinlet.h" + +class QSK_EXPORT QskFluent2TextInputSkinlet : public QskTextInputSkinlet +{ + Q_GADGET + + using Inherited = QskTextInputSkinlet; + + public: + Q_INVOKABLE QskFluent2TextInputSkinlet( QskSkin* = nullptr ); + ~QskFluent2TextInputSkinlet() override; + + QRectF subControlRect( const QskSkinnable*, + const QRectF& rect, QskAspect::Subcontrol ) const override; + + QSizeF adjustSizeHint( const QskSkinnable*, + Qt::SizeHint, const QSizeF& ) const override; +}; + +#endif diff --git a/designsystems/fusion/QskFusionSkin.cpp b/designsystems/fusion/QskFusionSkin.cpp index 1ad74048..358fb8df 100644 --- a/designsystems/fusion/QskFusionSkin.cpp +++ b/designsystems/fusion/QskFusionSkin.cpp @@ -386,7 +386,7 @@ void Editor::setupTextInput() using A = QskAspect; using P = QPalette; - setAlignment( Q::InputText, Qt::AlignLeft | Qt::AlignTop ); + setAlignment( Q::InputText, Qt::AlignLeft | Qt::AlignVCenter ); for ( auto state : { A::NoState, Q::Disabled } ) { diff --git a/designsystems/material3/CMakeLists.txt b/designsystems/material3/CMakeLists.txt index 051e8ed9..da9f1862 100644 --- a/designsystems/material3/CMakeLists.txt +++ b/designsystems/material3/CMakeLists.txt @@ -6,6 +6,7 @@ set(SOURCES QskMaterial3Global.h QskMaterial3Skin.h QskMaterial3Skin.cpp QskMaterial3SkinFactory.h QskMaterial3SkinFactory.cpp + QskMaterial3TextInputSkinlet.h QskMaterial3TextInputSkinlet.cpp ) qt_add_resources(SOURCES QskMaterial3Icons.qrc) diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index 0f63c219..b47b2e24 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -10,6 +10,8 @@ #include "QskMaterial3Skin.h" +#include "QskMaterial3TextInputSkinlet.h" + #include #include @@ -1736,6 +1738,11 @@ static inline QFont createFont( qreal size, int lineHeight, return font; } +void QskMaterial3Skin::setupSkinlets() +{ + declareSkinlet< QskTextInput, QskMaterial3TextInputSkinlet >(); +} + void QskMaterial3Skin::setupFonts() { /* @@ -1801,6 +1808,7 @@ void QskMaterial3Skin::initHints() { const QskMaterial3Theme theme( colorScheme() ); + setupSkinlets(); setupFonts(); setupGraphicFilters( theme ); diff --git a/designsystems/material3/QskMaterial3Skin.h b/designsystems/material3/QskMaterial3Skin.h index f6e7dcaf..6a02dce2 100644 --- a/designsystems/material3/QskMaterial3Skin.h +++ b/designsystems/material3/QskMaterial3Skin.h @@ -143,6 +143,7 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Skin : public QskSkin void initHints() override; private: + void setupSkinlets(); void setupFonts(); void setupGraphicFilters( const QskMaterial3Theme& ); void setGraphicColor( GraphicRole, QRgb ); diff --git a/designsystems/material3/QskMaterial3TextInputSkinlet.cpp b/designsystems/material3/QskMaterial3TextInputSkinlet.cpp new file mode 100644 index 00000000..040d506d --- /dev/null +++ b/designsystems/material3/QskMaterial3TextInputSkinlet.cpp @@ -0,0 +1,332 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskMaterial3TextInputSkinlet.h" +#include "QskTextInput.h" + +#include "QskBoxBorderColors.h" +#include "QskBoxBorderMetrics.h" +#include "QskBoxShapeMetrics.h" +#include "QskFunctions.h" + +#include + +using Q = QskTextInput; + +namespace +{ + QString maxLengthString( const QskTextInput* input ) + { + QString s = QString::number( input->inputText().length() ) + + " / " + QString::number( input->maxLength() ); + return s; + } + + // We need to "cut a hole" in the upper gradient for the label text: + QskBoxBorderColors outlineColors( const QskTextInput* input ) + { + auto borderColors = input->boxBorderColorsHint( Q::Panel ); + auto topGradient = borderColors.gradientAt( Qt::TopEdge ); + + const auto panelRect = input->subControlRect( Q::Panel ); + + const auto margins = input->marginHint( Q::LabelText ); + const auto iconMargins = input->marginHint( Q::LeadingIcon ); + + const auto x1 = iconMargins.left() - margins.left(); + const auto r1 = x1 / panelRect.width(); + + const auto w = qskHorizontalAdvance( input->effectiveFont( Q::LabelText ), input->labelText() ); + + const auto x2 = x1 + w + margins.right(); + const auto r2 = x2 / panelRect.width(); + + topGradient.setStops( { + { 0.0, topGradient.startColor() }, + { r1, topGradient.startColor() }, + { r1, Qt::transparent }, + { r2, Qt::transparent }, + { r2, topGradient.startColor() }, + { 1.0, topGradient.startColor() } + } ); + + borderColors.setGradientAt( Qt::TopEdge, topGradient ); + + return borderColors; + } +} + +QskMaterial3TextInputSkinlet::QskMaterial3TextInputSkinlet( QskSkin* skin ) + : Inherited( skin ) +{ + setNodeRoles( { + PanelRole, + LeadingIconRole, + LabelTextRole, + HintTextRole, + SupportingTextRole, + CharacterCountRole, + TrailingIconRippleRole, + TrailingIconRole, + } ); +} + +QskMaterial3TextInputSkinlet::~QskMaterial3TextInputSkinlet() +{ +} + +QRectF QskMaterial3TextInputSkinlet::subControlRect( const QskSkinnable* skinnable, + const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const +{ + const auto input = static_cast< const Q* >( skinnable ); + + if ( subControl == Q::Panel ) + { + auto rect = contentsRect; + + if( input->emphasis() == Q::LowEmphasis ) + { + const auto fontHeight = input->effectiveFontHeight( Q::LabelText | Q::Focused ); + rect.setY( fontHeight / 2 ); + } + + const auto h = input->strutSizeHint( subControl ).height(); + rect.setHeight( h ); + + return rect; + } + else if ( subControl == Q::LabelText ) + { + const auto inputRect = input->subControlRect( Q::InputText ); + + if( !input->inputText().isEmpty() + || input->hasSkinState( Q::Focused ) + || input->hasSkinState( Q::Editing ) ) + { + const auto margins = input->marginHint( subControl ); + auto rect = inputRect; + const QFontMetricsF fm( input->effectiveFont( subControl ) ); + + if( input->emphasis() == Q::LowEmphasis ) + { + const auto iconMargins = input->marginHint( Q::LeadingIcon ); + rect.setX( iconMargins.left() ); + rect.setY( 0 ); + } + else + { + rect.setY( contentsRect.y() + margins.top() ); + } + + rect.setHeight( fm.height() ); + + return rect; + } + else + { + return inputRect; + } + } + else if ( subControl == Q::LeadingIcon ) + { + if( input->symbolHint( subControl ).isEmpty() ) + { + return {}; + } + else + { + const auto margins = input->marginHint( subControl ); + const auto panelRect = input->subControlRect( Q::Panel ); + auto rect = panelRect.marginsRemoved( margins ); + + const auto size = input->strutSizeHint( subControl ); + rect.setSize( size ); + rect.moveCenter( { rect.center().x(), panelRect.center().y() } ); + + return rect; + } + } + else if ( subControl == Q::HintText ) + { + if( !input->hasSkinState( Q::TextPopulated ) + && ( input->hasSkinState( Q::Focused ) || input->hasSkinState( Q::Editing ) ) ) + { + return input->subControlRect( Q::InputText ); + } + else + { + return {}; + } + } + else if ( subControl == Q::SupportingText ) + { + if( input->supportingText().isEmpty() ) + { + return {}; + } + else + { + auto rect = contentsRect; + + const auto margins = input->marginHint( subControl ); + const auto h = margins.top() + input->effectiveFontHeight( subControl ) + margins.bottom(); + rect.setTop( rect.bottom() - h ); + + rect.setLeft( rect.left() + margins.left() ); + + return rect; + } + } + else if ( subControl == Q::CharacterCount ) + { + if( input->maxLength() == 32767 ) // magic number hardcoded in qquicktextinput.cpp + { + return {}; + } + else + { + auto rect = contentsRect; + + const auto margins = input->marginHint( subControl ); + const auto h = margins.top() + input->effectiveFontHeight( subControl ) + margins.bottom(); + rect.setTop( rect.bottom() - h ); + + const QFontMetricsF fm( input->effectiveFont( subControl ) ); + const auto w = qskHorizontalAdvance( fm, maxLengthString( input ) ); + rect.setRight( rect.right() - margins.right() ); + rect.setLeft( rect.right() - ( margins.left() + w + margins.right() ) ); + + return rect; + } + } + else if ( subControl == Q::TrailingIconRipple ) + { + const auto cursorPos = input->effectiveSkinHint( + Q::TrailingIconRipple | Q::Hovered | QskAspect::Metric | QskAspect::Position ).toPointF(); + const auto trailingIconRect = input->subControlRect( Q::TrailingIcon ); + + if( !cursorPos.isNull() && trailingIconRect.contains( cursorPos ) ) + { + const auto size = input->strutSizeHint( subControl ); + QRectF rect( { 0, 0 }, size ); + + rect.moveCenter( trailingIconRect.center() ); + + return rect; + } + else + { + return {}; + } + } + else if ( subControl == Q::TrailingIcon ) + { + if( input->symbolHint( subControl ).isEmpty() ) + { + return {}; + } + else + { + const auto margins = input->marginHint( subControl ); + const auto panelRect = input->subControlRect( Q::Panel ); + auto rect = panelRect.marginsRemoved( margins ); + + const auto size = input->strutSizeHint( subControl ); + rect.setHeight( size.height() ); + rect.moveCenter( { rect.center().x(), panelRect.center().y() } ); + rect.setLeft( rect.right() - size.width() ); + + return rect; + } + } + + return Inherited::subControlRect( skinnable, contentsRect, subControl ); +} + +QSizeF QskMaterial3TextInputSkinlet::adjustSizeHint( const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& oldHint ) const +{ + if ( which != Qt::PreferredSize ) + return QSizeF(); + + auto hint = oldHint; + + const auto input = static_cast< const Q* >( skinnable ); + + if( input->emphasis() == Q::LowEmphasis ) + { + const auto fontHeight = input->effectiveFontHeight( Q::LabelText | Q::Focused ); + hint.rheight() += fontHeight / 2; + } + + if( !input->supportingText().isEmpty() || input->maxLength() != 32767 ) // magic number hardcoded in qquicktextinput.cpp + { + const auto margins = input->marginHint( Q::SupportingText ); + hint.rheight() += margins.top() + input->effectiveFontHeight( Q::SupportingText ) + margins.bottom(); + } + + return hint; +} + +QSGNode* QskMaterial3TextInputSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + const auto input = static_cast< const Q* >( skinnable ); + + switch ( nodeRole ) + { + case PanelRole: + { + if ( !input->hasPanel() ) + return nullptr; + + if( input->emphasis() == Q::LowEmphasis + && ( input->hasSkinState( Q::TextPopulated ) + || input->hasSkinState( Q::Focused ) + || input->hasSkinState( Q::Editing ) ) ) + { + const auto shape = skinnable->boxShapeHint( Q::Panel ); + const auto borderMetrics = skinnable->boxBorderMetricsHint( Q::Panel ); + const auto borderColors = outlineColors( input ); + const auto gradient = input->gradientHint( Q::Panel ); + + return updateBoxNode( skinnable, node, input->subControlRect( Q::Panel ), + shape, borderMetrics, borderColors, gradient ); + } + else + { + return updateBoxNode( skinnable, node, Q::Panel ); + } + } + + case LeadingIconRole: + { + return updateSymbolNode( skinnable, node, Q::LeadingIcon ); + } + + case SupportingTextRole: + { + return updateTextNode( skinnable, node, input->supportingText(), Q::SupportingText ); + } + + case CharacterCountRole: + { + return updateTextNode( skinnable, node, maxLengthString( input ), Q::CharacterCount ); + } + + case TrailingIconRippleRole: + { + return updateBoxNode( skinnable, node, Q::TrailingIconRipple ); + } + + case TrailingIconRole: + { + return updateSymbolNode( skinnable, node, Q::TrailingIcon ); + } + } + + return Inherited::updateSubNode( skinnable, nodeRole, node ); +} + +#include "moc_QskMaterial3TextInputSkinlet.cpp" diff --git a/designsystems/material3/QskMaterial3TextInputSkinlet.h b/designsystems/material3/QskMaterial3TextInputSkinlet.h new file mode 100644 index 00000000..54bc47c7 --- /dev/null +++ b/designsystems/material3/QskMaterial3TextInputSkinlet.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_MATERIAL3_INPUT_SKINLET_H +#define QSK_MATERIAL3_INPUT_SKINLET_H + +#include "QskTextInputSkinlet.h" + +class QSK_EXPORT QskMaterial3TextInputSkinlet : public QskTextInputSkinlet +{ + Q_GADGET + + using Inherited = QskTextInputSkinlet; + + public: + enum NodeRole + { + LeadingIconRole = Inherited::RoleCount, + SupportingTextRole, + TrailingIconRippleRole, + TrailingIconRole, + CharacterCountRole + }; + + Q_INVOKABLE QskMaterial3TextInputSkinlet( QskSkin* = nullptr ); + ~QskMaterial3TextInputSkinlet() override; + + QRectF subControlRect( const QskSkinnable*, + const QRectF& rect, QskAspect::Subcontrol ) const override; + + QSizeF adjustSizeHint( const QskSkinnable*, + Qt::SizeHint, const QSizeF& ) const override; + + protected: + QSGNode* updateSubNode( const QskSkinnable*, + quint8 nodeRole, QSGNode* ) const override; +}; + +#endif diff --git a/examples/gallery/inputs/InputPage.cpp b/examples/gallery/inputs/InputPage.cpp index d539433f..c87ec609 100644 --- a/examples/gallery/inputs/InputPage.cpp +++ b/examples/gallery/inputs/InputPage.cpp @@ -96,7 +96,7 @@ namespace auto input = new QskTextInput( this ); input->setEmphasis( emphasis ); input->setLeadingIcon( {} ); - input->setLabelText( "no hint text" ); + input->setHintText( "no label text" ); } { diff --git a/src/controls/QskTextInput.cpp b/src/controls/QskTextInput.cpp index 3971545e..e821bf4e 100644 --- a/src/controls/QskTextInput.cpp +++ b/src/controls/QskTextInput.cpp @@ -7,6 +7,7 @@ #include "QskEvent.h" #include "QskFontRole.h" #include "QskQuick.h" +#include "QskTextInputSkinlet.h" QSK_QT_PRIVATE_BEGIN #include @@ -567,19 +568,8 @@ QSizeF QskTextInput::layoutSizeHint( Qt::SizeHint which, const QSizeF& ) const hint = hint.expandedTo( strutSizeHint( Panel ) ); } - if( emphasis() == LowEmphasis ) - { - const auto fontHeight = effectiveFontHeight( LabelText | Focused ); - hint.rheight() += fontHeight / 2; - } - - if( !supportingText().isEmpty() || maxLength() != 32767 ) // magic number hardcoded in qquicktextinput.cpp - { - const auto margins = marginHint( SupportingText ); - hint.rheight() += margins.top() + effectiveFontHeight( SupportingText ) + margins.bottom(); - } - - return hint; + const auto textInputSkinlet = static_cast< const QskTextInputSkinlet* >( effectiveSkinlet() ); + return textInputSkinlet->adjustSizeHint( this, which, hint ); } void QskTextInput::updateLayout() diff --git a/src/controls/QskTextInputSkinlet.cpp b/src/controls/QskTextInputSkinlet.cpp index 2e48074b..8f56bca6 100644 --- a/src/controls/QskTextInputSkinlet.cpp +++ b/src/controls/QskTextInputSkinlet.cpp @@ -15,61 +15,13 @@ using Q = QskTextInput; -namespace -{ - QString maxLengthString( const QskTextInput* input ) - { - QString s = QString::number( input->inputText().length() ) - + " / " + QString::number( input->maxLength() ); - return s; - } - - // We need to "cut a hole" in the upper gradient for the label text: - QskBoxBorderColors outlineColors( const QskTextInput* input ) - { - auto borderColors = input->boxBorderColorsHint( Q::Panel ); - auto topGradient = borderColors.gradientAt( Qt::TopEdge ); - - const auto panelRect = input->subControlRect( Q::Panel ); - - const auto margins = input->marginHint( Q::LabelText ); - const auto iconMargins = input->marginHint( Q::LeadingIcon ); - - const auto x1 = iconMargins.left() - margins.left(); - const auto r1 = x1 / panelRect.width(); - - const auto w = qskHorizontalAdvance( input->effectiveFont( Q::LabelText ), input->labelText() ); - - const auto x2 = x1 + w + margins.right(); - const auto r2 = x2 / panelRect.width(); - - topGradient.setStops( { - { 0.0, topGradient.startColor() }, - { r1, topGradient.startColor() }, - { r1, Qt::transparent }, - { r2, Qt::transparent }, - { r2, topGradient.startColor() }, - { 1.0, topGradient.startColor() } - } ); - - borderColors.setGradientAt( Qt::TopEdge, topGradient ); - - return borderColors; - } -} - QskTextInputSkinlet::QskTextInputSkinlet( QskSkin* skin ) : Inherited( skin ) { setNodeRoles( { PanelRole, - LeadingIconRole, LabelTextRole, HintTextRole, - SupportingTextRole, - CharacterCountRole, - TrailingIconRippleRole, - TrailingIconRole, } ); } @@ -84,70 +36,7 @@ QRectF QskTextInputSkinlet::subControlRect( const QskSkinnable* skinnable, if ( subControl == Q::Panel ) { - auto rect = contentsRect; - - if( input->emphasis() == Q::LowEmphasis ) - { - const auto fontHeight = input->effectiveFontHeight( Q::LabelText | Q::Focused ); - rect.setY( fontHeight / 2 ); - } - - - const auto h = input->strutSizeHint( subControl ).height(); - rect.setHeight( h ); - - return rect; - } - else if ( subControl == Q::LeadingIcon ) - { - if( input->symbolHint( subControl ).isEmpty() ) - { - return {}; - } - else - { - const auto margins = input->marginHint( subControl ); - const auto panelRect = input->subControlRect( Q::Panel ); - auto rect = panelRect.marginsRemoved( margins ); - - const auto size = input->strutSizeHint( subControl ); - rect.setSize( size ); - rect.moveCenter( { rect.center().x(), panelRect.center().y() } ); - - return rect; - } - } - else if ( subControl == Q::LabelText ) - { - const auto inputRect = input->subControlRect( Q::InputText ); - - if( !input->inputText().isEmpty() - || input->hasSkinState( Q::Focused ) - || input->hasSkinState( Q::Editing ) ) - { - const auto margins = input->marginHint( subControl ); - auto rect = inputRect; - const QFontMetricsF fm( input->effectiveFont( subControl ) ); - - if( input->emphasis() == Q::LowEmphasis ) - { - const auto iconMargins = input->marginHint( Q::LeadingIcon ); - rect.setX( iconMargins.left() ); - rect.setY( 0 ); - } - else - { - rect.setY( contentsRect.y() + margins.top() ); - } - - rect.setHeight( fm.height() ); - - return rect; - } - else - { - return inputRect; - } + return contentsRect; } else if ( subControl == Q::InputText ) { @@ -173,103 +62,15 @@ QRectF QskTextInputSkinlet::subControlRect( const QskSkinnable* skinnable, return rect; } - else if ( subControl == Q::HintText ) - { - if( !input->hasSkinState( Q::TextPopulated ) - && ( input->hasSkinState( Q::Focused ) || input->hasSkinState( Q::Editing ) ) ) - { - return input->subControlRect( Q::InputText ); - } - else - { - return {}; - } - } - else if ( subControl == Q::SupportingText ) - { - if( input->supportingText().isEmpty() ) - { - return {}; - } - else - { - auto rect = contentsRect; - - const auto margins = input->marginHint( subControl ); - const auto h = margins.top() + input->effectiveFontHeight( subControl ) + margins.bottom(); - rect.setTop( rect.bottom() - h ); - - rect.setLeft( rect.left() + margins.left() ); - - return rect; - } - } - else if ( subControl == Q::CharacterCount ) - { - if( input->maxLength() == 32767 ) // magic number hardcoded in qquicktextinput.cpp - { - return {}; - } - else - { - auto rect = contentsRect; - - const auto margins = input->marginHint( subControl ); - const auto h = margins.top() + input->effectiveFontHeight( subControl ) + margins.bottom(); - rect.setTop( rect.bottom() - h ); - - const QFontMetricsF fm( input->effectiveFont( subControl ) ); - const auto w = qskHorizontalAdvance( fm, maxLengthString( input ) ); - rect.setRight( rect.right() - margins.right() ); - rect.setLeft( rect.right() - ( margins.left() + w + margins.right() ) ); - - return rect; - } - } - else if ( subControl == Q::TrailingIconRipple ) - { - const auto cursorPos = input->effectiveSkinHint( - Q::TrailingIconRipple | Q::Hovered | QskAspect::Metric | QskAspect::Position ).toPointF(); - const auto trailingIconRect = input->subControlRect( Q::TrailingIcon ); - - if( !cursorPos.isNull() && trailingIconRect.contains( cursorPos ) ) - { - const auto size = input->strutSizeHint( subControl ); - QRectF rect( { 0, 0 }, size ); - - rect.moveCenter( trailingIconRect.center() ); - - return rect; - } - else - { - return {}; - } - } - else if ( subControl == Q::TrailingIcon ) - { - if( input->symbolHint( subControl ).isEmpty() ) - { - return {}; - } - else - { - const auto margins = input->marginHint( subControl ); - const auto panelRect = input->subControlRect( Q::Panel ); - auto rect = panelRect.marginsRemoved( margins ); - - const auto size = input->strutSizeHint( subControl ); - rect.setHeight( size.height() ); - rect.moveCenter( { rect.center().x(), panelRect.center().y() } ); - rect.setLeft( rect.right() - size.width() ); - - return rect; - } - } return Inherited::subControlRect( skinnable, contentsRect, subControl ); } +QSizeF QskTextInputSkinlet::adjustSizeHint( const QskSkinnable*, Qt::SizeHint, const QSizeF& hint ) const +{ + return hint; +} + QSGNode* QskTextInputSkinlet::updateSubNode( const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const { @@ -282,28 +83,7 @@ QSGNode* QskTextInputSkinlet::updateSubNode( if ( !input->hasPanel() ) return nullptr; - if( input->emphasis() == Q::LowEmphasis - && ( input->hasSkinState( Q::TextPopulated ) - || input->hasSkinState( Q::Focused ) - || input->hasSkinState( Q::Editing ) ) ) - { - const auto shape = skinnable->boxShapeHint( Q::Panel ); - const auto borderMetrics = skinnable->boxBorderMetricsHint( Q::Panel ); - const auto borderColors = outlineColors( input ); - const auto gradient = input->gradientHint( Q::Panel ); - - return updateBoxNode( skinnable, node, input->subControlRect( Q::Panel ), - shape, borderMetrics, borderColors, gradient ); - } - else - { - return updateBoxNode( skinnable, node, Q::Panel ); - } - } - - case LeadingIconRole: - { - return updateSymbolNode( skinnable, node, Q::LeadingIcon ); + return updateBoxNode( skinnable, node, Q::Panel ); } case LabelTextRole: @@ -315,26 +95,6 @@ QSGNode* QskTextInputSkinlet::updateSubNode( { return updateTextNode( skinnable, node, input->hintText(), Q::HintText ); } - - case SupportingTextRole: - { - return updateTextNode( skinnable, node, input->supportingText(), Q::SupportingText ); - } - - case CharacterCountRole: - { - return updateTextNode( skinnable, node, maxLengthString( input ), Q::CharacterCount ); - } - - case TrailingIconRippleRole: - { - return updateBoxNode( skinnable, node, Q::TrailingIconRipple ); - } - - case TrailingIconRole: - { - return updateSymbolNode( skinnable, node, Q::TrailingIcon ); - } } return Inherited::updateSubNode( skinnable, nodeRole, node ); diff --git a/src/controls/QskTextInputSkinlet.h b/src/controls/QskTextInputSkinlet.h index 904bb05c..eec8dbc1 100644 --- a/src/controls/QskTextInputSkinlet.h +++ b/src/controls/QskTextInputSkinlet.h @@ -18,13 +18,8 @@ class QSK_EXPORT QskTextInputSkinlet : public QskSkinlet enum NodeRole { PanelRole, - LeadingIconRole, LabelTextRole, HintTextRole, - SupportingTextRole, - TrailingIconRippleRole, - TrailingIconRole, - CharacterCountRole, RoleCount }; @@ -34,6 +29,9 @@ class QSK_EXPORT QskTextInputSkinlet : public QskSkinlet QRectF subControlRect( const QskSkinnable*, const QRectF& rect, QskAspect::Subcontrol ) const override; + virtual QSizeF adjustSizeHint( const QskSkinnable*, + Qt::SizeHint, const QSizeF& ) const; + protected: QSGNode* updateSubNode( const QskSkinnable*, quint8 nodeRole, QSGNode* ) const override; From cb7f9c544267be0dece0467ff5259c09011757a0 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Tue, 15 Oct 2024 12:34:31 +0200 Subject: [PATCH 08/11] input text: fix CI build etc. --- designsystems/fluent2/QskFluent2TextInputSkinlet.h | 7 ++++--- designsystems/material3/QskMaterial3Skin.cpp | 1 + designsystems/material3/QskMaterial3TextInputSkinlet.cpp | 6 ++++-- designsystems/material3/QskMaterial3TextInputSkinlet.h | 3 ++- src/controls/QskTextInput.cpp | 2 +- src/controls/QskTextInput.h | 8 +++++--- 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/designsystems/fluent2/QskFluent2TextInputSkinlet.h b/designsystems/fluent2/QskFluent2TextInputSkinlet.h index 79e9cd26..17cda6e8 100644 --- a/designsystems/fluent2/QskFluent2TextInputSkinlet.h +++ b/designsystems/fluent2/QskFluent2TextInputSkinlet.h @@ -3,12 +3,13 @@ * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ -#ifndef QSK_MATERIAL3_INPUT_SKINLET_H -#define QSK_MATERIAL3_INPUT_SKINLET_H +#ifndef QSK_FLUENT2_INPUT_SKINLET_H +#define QSK_FLUENT2_INPUT_SKINLET_H +#include "QskFluent2Global.h" #include "QskTextInputSkinlet.h" -class QSK_EXPORT QskFluent2TextInputSkinlet : public QskTextInputSkinlet +class QSK_FLUENT2_EXPORT QskFluent2TextInputSkinlet : public QskTextInputSkinlet { Q_GADGET diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index b47b2e24..1fcef2f2 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -502,6 +502,7 @@ void Editor::setupTextInput() setBoxBorderMetrics( Q::Panel | M3::Outlined | Q::Focused, 2_dp, allStates ); setBoxBorderColors( Q::Panel | M3::Outlined | Q::Focused, m_pal.primary, allStates ); + setBoxBorderColors( Q::Panel | M3::Outlined | Q::Error, m_pal.error, allStates ); // LeadingIcon diff --git a/designsystems/material3/QskMaterial3TextInputSkinlet.cpp b/designsystems/material3/QskMaterial3TextInputSkinlet.cpp index 040d506d..44c1b74d 100644 --- a/designsystems/material3/QskMaterial3TextInputSkinlet.cpp +++ b/designsystems/material3/QskMaterial3TextInputSkinlet.cpp @@ -36,12 +36,14 @@ namespace const auto iconMargins = input->marginHint( Q::LeadingIcon ); const auto x1 = iconMargins.left() - margins.left(); - const auto r1 = x1 / panelRect.width(); + auto r1 = x1 / panelRect.width(); + r1 = qBound( 0.0, r1, 1.0 ); const auto w = qskHorizontalAdvance( input->effectiveFont( Q::LabelText ), input->labelText() ); const auto x2 = x1 + w + margins.right(); - const auto r2 = x2 / panelRect.width(); + auto r2 = x2 / panelRect.width(); + r2 = qBound( 0.0, r2, 1.0 ); topGradient.setStops( { { 0.0, topGradient.startColor() }, diff --git a/designsystems/material3/QskMaterial3TextInputSkinlet.h b/designsystems/material3/QskMaterial3TextInputSkinlet.h index 54bc47c7..88077239 100644 --- a/designsystems/material3/QskMaterial3TextInputSkinlet.h +++ b/designsystems/material3/QskMaterial3TextInputSkinlet.h @@ -6,9 +6,10 @@ #ifndef QSK_MATERIAL3_INPUT_SKINLET_H #define QSK_MATERIAL3_INPUT_SKINLET_H +#include "QskMaterial3Global.h" #include "QskTextInputSkinlet.h" -class QSK_EXPORT QskMaterial3TextInputSkinlet : public QskTextInputSkinlet +class QSK_MATERIAL3_EXPORT QskMaterial3TextInputSkinlet : public QskTextInputSkinlet { Q_GADGET diff --git a/src/controls/QskTextInput.cpp b/src/controls/QskTextInput.cpp index e821bf4e..149fb12c 100644 --- a/src/controls/QskTextInput.cpp +++ b/src/controls/QskTextInput.cpp @@ -445,7 +445,7 @@ void QskTextInput::keyReleaseEvent( QKeyEvent* event ) void QskTextInput::mousePressEvent( QMouseEvent* event ) { - if( !isReadOnly() && subControlContentsRect( TrailingIcon ).contains( event->position() ) ) + if( !isReadOnly() && subControlContentsRect( TrailingIcon ).contains( event->pos() ) ) { setInputText( {} ); } diff --git a/src/controls/QskTextInput.h b/src/controls/QskTextInput.h index 41e0fe05..91f4e74e 100644 --- a/src/controls/QskTextInput.h +++ b/src/controls/QskTextInput.h @@ -17,9 +17,11 @@ class QSK_EXPORT QskTextInput : public QskControl { Q_OBJECT - Q_PROPERTY( QString inputText READ inputText WRITE setInputText NOTIFY inputTextChanged USER true ) + Q_PROPERTY( QString inputText READ inputText + WRITE setInputText NOTIFY inputTextChanged USER true ) - Q_PROPERTY( QString labelText READ labelText WRITE setLabelText NOTIFY labelTextChanged ) + Q_PROPERTY( QString labelText READ labelText + WRITE setLabelText NOTIFY labelTextChanged ) Q_PROPERTY( QString hintText READ hintText WRITE setHintText NOTIFY hintTextChanged ) @@ -103,7 +105,7 @@ class QSK_EXPORT QskTextInput : public QskControl Q_ENUM( EchoMode ) QskTextInput( QQuickItem* parent = nullptr ); - QskTextInput( const QString&, QQuickItem* parent = nullptr ); // ### do we need this constructor? + QskTextInput( const QString&, QQuickItem* parent = nullptr ); ~QskTextInput() override; From da1566c0c83bcd09db9989345fcf9bb8068a057a Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Mon, 11 Nov 2024 09:57:51 +0100 Subject: [PATCH 09/11] text input: Fix build after rebase --- examples/gallery/inputs/InputPage.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/examples/gallery/inputs/InputPage.cpp b/examples/gallery/inputs/InputPage.cpp index c87ec609..30edcb96 100644 --- a/examples/gallery/inputs/InputPage.cpp +++ b/examples/gallery/inputs/InputPage.cpp @@ -65,7 +65,7 @@ namespace { public: TextInputBox( QQuickItem* parent = nullptr ) - : QskLinearBox( Qt::Horizontal, parent ) + : QskLinearBox( Qt::Horizontal, 3, parent ) { setSpacing( 25 ); setDefaultAlignment( Qt::AlignHCenter | Qt::AlignTop ); @@ -150,21 +150,16 @@ InputPage::InputPage( QQuickItem* parent ) spinBox->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed ); auto textInputBox = new TextInputBox(); - inputBox->setSizePolicy( Qt::Vertical, QskSizePolicy::Fixed ); + textInputBox->setSizePolicy( Qt::Vertical, QskSizePolicy::Fixed ); auto vBox = new QskLinearBox( Qt::Vertical ); vBox->setSpacing( 30 ); vBox->setExtraSpacingAt( Qt::RightEdge | Qt::BottomEdge ); - auto spinBox = new QskSpinBox( 0.0, 100.0, 1.0 ); - spinBox->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed ); - spinBox->setPageSize( 5 ); - spinBox->setValue( 35 ); - vBox->addItem( sliders[0].continous ); vBox->addItem( sliders[0].discrete ); - vBox->addItem( inputBox ); vBox->addItem( spinBox ); + vBox->addItem( textInputBox ); auto mainBox = new QskLinearBox( Qt::Horizontal, this ); mainBox->setSpacing( 30 ); From 386c679dfb117f5fa60e327ccba5184c4f40589a Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Mon, 11 Nov 2024 10:24:58 +0100 Subject: [PATCH 10/11] M3 input text: Don't cut gradient if label text is empty Resolves #400 --- designsystems/material3/QskMaterial3TextInputSkinlet.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/designsystems/material3/QskMaterial3TextInputSkinlet.cpp b/designsystems/material3/QskMaterial3TextInputSkinlet.cpp index 44c1b74d..1a5c228f 100644 --- a/designsystems/material3/QskMaterial3TextInputSkinlet.cpp +++ b/designsystems/material3/QskMaterial3TextInputSkinlet.cpp @@ -28,6 +28,12 @@ namespace QskBoxBorderColors outlineColors( const QskTextInput* input ) { auto borderColors = input->boxBorderColorsHint( Q::Panel ); + + if( input->labelText().isEmpty() ) + { + return borderColors; + } + auto topGradient = borderColors.gradientAt( Qt::TopEdge ); const auto panelRect = input->subControlRect( Q::Panel ); From 9f9952fbf4aa84ebe220b76effda9788bd1a0d0a Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Mon, 11 Nov 2024 10:56:52 +0100 Subject: [PATCH 11/11] M3 input text: Style selection Resolves #466 --- designsystems/material3/QskMaterial3Skin.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index 1fcef2f2..1ae75711 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -543,6 +543,7 @@ void Editor::setupTextInput() setMargin( Q::InputText, { 16_dp, 8_dp, 16_dp, 8_dp } ); setColor( Q::InputText, m_pal.onSurface ); + setColor( Q::Panel | Q::Selected, m_pal.surfaceVariant, allStates ); setFontRole( Q::InputText, BodyLarge ); setAlignment( Q::InputText, Qt::AlignLeft | Qt::AlignBottom ); @@ -552,6 +553,7 @@ void Editor::setupTextInput() // InputText - Outlined + setColor( Q::Panel | Q::Selected | M3::Outlined, m_pal.surfaceVariant, allStates ); setAlignment( Q::InputText | M3::Outlined, Qt::AlignLeft | Qt::AlignVCenter );