diff --git a/designsystems/fluent2/CMakeLists.txt b/designsystems/fluent2/CMakeLists.txt index 1aaec81a..799c360a 100644 --- a/designsystems/fluent2/CMakeLists.txt +++ b/designsystems/fluent2/CMakeLists.txt @@ -7,15 +7,10 @@ list(APPEND HEADERS QskFluent2Global.h QskFluent2Theme.h QskFluent2SkinFactory.h ) -list(APPEND PRIVATE_HEADERS - QskFluent2TextFieldSkinlet.h -) - list(APPEND SOURCES QskFluent2Theme.cpp QskFluent2Skin.cpp QskFluent2SkinFactory.cpp - QskFluent2TextFieldSkinlet.cpp ) qt_add_resources(SOURCES QskFluent2Icons.qrc) diff --git a/designsystems/fluent2/QskFluent2Skin.cpp b/designsystems/fluent2/QskFluent2Skin.cpp index 346eb79a..c683dff1 100644 --- a/designsystems/fluent2/QskFluent2Skin.cpp +++ b/designsystems/fluent2/QskFluent2Skin.cpp @@ -47,7 +47,6 @@ */ #include "QskFluent2Skin.h" #include "QskFluent2Theme.h" -#include "QskFluent2TextFieldSkinlet.h" #include #include @@ -1854,11 +1853,13 @@ void Editor::setupTextAreaColors( setBoxBorderGradient( panel, borderColor1, borderColor2, panelColor ); } } + void Editor::setupTextFieldMetrics() { using Q = QskTextField; - setFontRole( Q::Text, Fluent2::Body ); + setFontRole( Q::Header, Fluent2::Body ); + setFontRole( Q::Footer, Fluent2::Caption ); setStrutSize( Q::TextPanel, { -1, 30_px } ); setPadding( Q::TextPanel, { 11_px, 0, 11_px, 0 } ); @@ -1869,15 +1870,14 @@ void Editor::setupTextFieldMetrics() setBoxShape( Q::TextPanel, 3_px ); - setAlignment( Q::Placeholder, Qt::AlignLeft | Qt::AlignVCenter ); - setFontRole( Q::Placeholder, fontRole( Q::Text ) ); - - setStrutSize( Q::Header, { -1, 30_px } ); - setFontRole( Q::Header, Fluent2::Body ); - - setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter ); - setFontRole( Q::Text, Fluent2::Body ); + for ( const auto subControl : { Q::Text, Q::Placeholder } ) + { + setFontRole( subControl, Fluent2::Body ); + setAlignment( subControl, Qt::AlignLeft | Qt::AlignVCenter ); + } + //setStrutSize( Q::Header, { -1, 30_px } ); + //setStrutSize( Q::Footer, { -1, 30_px } ); setSymbol( Q::Icon, symbol( "search" ) ); setSymbol( Q::Button, symbol( "dismiss" ) ); @@ -2141,8 +2141,6 @@ void Editor::setupVirtualKeyboardColors( QskFluent2Skin::QskFluent2Skin( QObject* parent ) : Inherited( parent ) { - declareSkinlet< QskTextField, QskFluent2TextFieldSkinlet >(); - setupFonts(); Editor editor( &hintTable() ); diff --git a/designsystems/fluent2/QskFluent2TextFieldSkinlet.cpp b/designsystems/fluent2/QskFluent2TextFieldSkinlet.cpp deleted file mode 100644 index f05cb44e..00000000 --- a/designsystems/fluent2/QskFluent2TextFieldSkinlet.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) The authors - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "QskFluent2TextFieldSkinlet.h" -#include "QskTextField.h" - -using Q = QskTextField; - -QskFluent2TextFieldSkinlet::QskFluent2TextFieldSkinlet( QskSkin* skin ) - : Inherited( skin ) -{ -} - -QskFluent2TextFieldSkinlet::~QskFluent2TextFieldSkinlet() -{ -} - -QRectF QskFluent2TextFieldSkinlet::subControlRect( const QskSkinnable* skinnable, - const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const -{ - if ( subControl == Q::TextPanel ) - { - auto rect = subControlRect( skinnable, contentsRect, Q::Panel ); - rect.setY( rect.bottom() - skinnable->strutSizeHint( subControl ).height() ); - - return rect; - } - - if ( subControl == Q::Header ) - { - const auto rect = subControlRect( skinnable, contentsRect, Q::TextPanel ); - const auto h = skinnable->effectiveFontHeight( Q::Header ); - - return QRectF( rect.x(), rect.y() - h, rect.width(), h ); - } - - return Inherited::subControlRect( skinnable, contentsRect, subControl ); -} - -QSizeF QskFluent2TextFieldSkinlet::sizeHint( const QskSkinnable* skinnable, - Qt::SizeHint which, const QSizeF& constraint ) const -{ - if ( which != Qt::PreferredSize ) - return QSizeF(); - - auto hint = Inherited::sizeHint( skinnable, which, constraint ); - - const auto textField = static_cast< const QskTextField* >( skinnable ); - if ( !textField->headerText().isEmpty() ) - { - // spacing ??? - hint.rheight() += textField->strutSizeHint( Q::Header ).height(); - } - - return hint; -} - -#include "moc_QskFluent2TextFieldSkinlet.cpp" diff --git a/designsystems/fluent2/QskFluent2TextFieldSkinlet.h b/designsystems/fluent2/QskFluent2TextFieldSkinlet.h deleted file mode 100644 index d6fc18b7..00000000 --- a/designsystems/fluent2/QskFluent2TextFieldSkinlet.h +++ /dev/null @@ -1,29 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) The authors - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#ifndef QSK_FLUENT2_TEXTFIELD_SKINLET_H -#define QSK_FLUENT2_TEXTFIELD_SKINLET_H - -#include "QskFluent2Global.h" -#include "QskTextFieldSkinlet.h" - -class QSK_FLUENT2_EXPORT QskFluent2TextFieldSkinlet : public QskTextFieldSkinlet -{ - Q_GADGET - - using Inherited = QskTextFieldSkinlet; - - public: - Q_INVOKABLE QskFluent2TextFieldSkinlet( QskSkin* = nullptr ); - ~QskFluent2TextFieldSkinlet() override; - - QRectF subControlRect( const QskSkinnable*, - const QRectF& rect, QskAspect::Subcontrol ) const override; - - QSizeF sizeHint( const QskSkinnable*, - Qt::SizeHint, const QSizeF& ) const override; -}; - -#endif diff --git a/designsystems/fusion/QskFusionSkin.cpp b/designsystems/fusion/QskFusionSkin.cpp index 1879782a..62404f17 100644 --- a/designsystems/fusion/QskFusionSkin.cpp +++ b/designsystems/fusion/QskFusionSkin.cpp @@ -425,6 +425,9 @@ void Editor::setupTextField() setBoxShape( Q::TextPanel, 2_px ); setPadding( Q::TextPanel, 4_px ); + + setFontRole( Q::Header, QskFontRole::Body ); + setFontRole( Q::Footer, QskFontRole::Caption ); } void Editor::setupTextArea() diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index f87da416..c0f77ea1 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -489,66 +489,55 @@ void Editor::setupTextField() const auto activeStates = Q::Focused | Q::Editing; { - // Text + // TextPanel setAnimation( Q::TextPanel | A::Color, qskDuration ); setAnimation( Q::TextPanel | A::Metric, qskDuration ); } - for ( const auto variation : { A::NoVariation, Filled, Outlined } ) { - const auto Panel = Q::Panel | variation; + const auto aspect = Q::TextPanel | Filled; - QskBoxBorderMetrics borderMetrics[2]; + setBoxBorderColors( aspect, m_pal.onSurfaceVariant ); + setGradient( aspect, m_pal.surfaceVariant ); - if ( variation == Filled ) - { - setBoxShape( Panel, m_pal.shapeExtraSmallTop ); + setGradient( aspect | Q::Hovered, + m_pal.hoverColor( m_pal.onSurfaceVariant, m_pal.surfaceVariant ), + { QskStateCombination::CombinationNoState, activeStates | Q::Error } ); - borderMetrics[0].setBottom( 1 ); - borderMetrics[1].setBottom( 2 ); + setGradient( aspect | Q::Disabled, + QskRgb::toTransparentF( m_pal.onSurface, 0.04 ) ); - setBoxBorderColors( Panel, m_pal.onSurfaceVariant ); - - setGradient( Panel, m_pal.surfaceVariant ); - - setGradient( Panel | Q::Hovered, - m_pal.hoverColor( m_pal.onSurfaceVariant, m_pal.surfaceVariant ), - { QskStateCombination::CombinationNoState, activeStates | Q::Error } ); - - setGradient( Panel | Q::Disabled, - QskRgb::toTransparentF( m_pal.onSurface, 0.04 ) ); - } - else - { - setBoxShape( Panel, m_pal.shapeExtraSmall ); - - borderMetrics[0].setWidths( 1 ); - borderMetrics[1].setWidths( 2 ); - - setBoxBorderColors( Panel, m_pal.outline ); - } - - if ( variation != A::NoVariation ) - { - setStrutSize( Panel, -1.0, 56_px ); - setPadding( Panel, 16_px, 8_px, 16_px, 8_px ); - } - - setBoxBorderMetrics( Panel, borderMetrics[0] ); - setBoxBorderMetrics( Panel, borderMetrics[1], activeStates | Q::Hovered ); - setBoxBorderMetrics( Panel | Q::Error, borderMetrics[1], activeStates | Q::Hovered ); - - setBoxBorderColors( Panel, m_pal.primary, activeStates ); - setBoxBorderColors( Panel | Q::Hovered, m_pal.primary, activeStates ); - setBoxBorderColors( Panel | Q::Hovered, m_pal.onSurface ); - setBoxBorderColors( Panel | Q::Disabled, m_pal.onSurface38 ); - - setBoxBorderColors( Panel | Q::Error, m_pal.error, - { QskStateCombination::CombinationNoState, activeStates | Q::Hovered } ); - - setColor( Q::TextPanel | variation | Q::Selected, m_pal.primary12 ); + setBoxShape( aspect, m_pal.shapeExtraSmallTop ); + setBoxBorderMetrics( aspect, { 0, 0, 0, 1 } ); + setBoxBorderMetrics( aspect, { 0, 0, 0, 2 }, activeStates | Q::Hovered ); + setBoxBorderMetrics( aspect | Q::Error, { 0, 0, 0, 2 }, activeStates | Q::Hovered ); } + { + const auto aspect = Q::TextPanel | Outlined; + + setBoxBorderColors( aspect, m_pal.outline ); + + setBoxShape( aspect, m_pal.shapeExtraSmall ); + setBoxBorderMetrics( aspect, 1 ); + setBoxBorderMetrics( aspect, 2, activeStates | Q::Hovered ); + setBoxBorderMetrics( aspect | Q::Error, 2, activeStates | Q::Hovered ); + + setGradient( aspect, QColor() ); + } + + setStrutSize( Q::TextPanel, -1.0, 56_px ); + setPadding( Q::TextPanel, 16_px, 8_px, 16_px, 8_px ); + + setBoxBorderColors( Q::TextPanel, m_pal.primary, activeStates ); + setBoxBorderColors( Q::TextPanel | Q::Hovered, m_pal.primary, activeStates ); + setBoxBorderColors( Q::TextPanel | Q::Hovered, m_pal.onSurface ); + setBoxBorderColors( Q::TextPanel | Q::Disabled, m_pal.onSurface38 ); + + setBoxBorderColors( Q::TextPanel | Q::Error, m_pal.error, + { QskStateCombination::CombinationNoState, activeStates | Q::Hovered } ); + + setColor( Q::TextPanel | Q::Selected, m_pal.primary12 ); // Icon @@ -561,39 +550,6 @@ void Editor::setupTextField() setGraphicRole( Q::Icon | Q::Disabled, M3::GraphicRoleOnSurface38 ); - { - setAlignment( Q::Header, Qt::AlignLeft | Qt::AlignVCenter ); - setFontRole( Q::Header, BodySmall ); - - setColor( Q::Header, m_pal.onSurfaceVariant ); - setColor( Q::Header, m_pal.primary, activeStates ); - setColor( Q::Header | Q::Error, m_pal.error ); - setColor( Q::Header | Q::Disabled, m_pal.onSurface38 ); - } - -#if 0 - setMargin( Q::Header | Outlined, 4_px, 0, 4_px, 0 ); -#endif - - for ( const auto subControl : { Q::Text, Q::Placeholder } ) - { - setAlignment( subControl, Qt::AlignLeft | Qt::AlignVCenter ); - - setFontRole( subControl, BodyLarge ); - - setColor( subControl | Q::Disabled, m_pal.onSurface38 ); - - if ( subControl == Q::Text ) - { - setColor( subControl, m_pal.onSurface ); - } - else - { - setColor( subControl | Q::Error, m_pal.error ); - setColor( subControl | Q::Error | Q::Hovered, m_pal.onSurface ); - } - } - // Button setStrutSize( Q::Button, { 24_px, 24_px } ); @@ -613,24 +569,48 @@ void Editor::setupTextField() setGradient( Q::ButtonPanel | Q::Hovered, m_pal.onSurface8 ); setBoxShape( Q::ButtonPanel, 100, Qt::RelativeSize ); + // Header - // SupportingText + setAlignment( Q::Header, Qt::AlignLeft | Qt::AlignVCenter ); + setFontRole( Q::Header, BodySmall ); + + setColor( Q::Header, m_pal.onSurfaceVariant ); + setColor( Q::Header, m_pal.primary, activeStates ); + setColor( Q::Header | Q::Error, m_pal.error ); + setColor( Q::Header | Q::Disabled, m_pal.onSurface38 ); + + for ( const auto subControl : { Q::Text, Q::Placeholder } ) + { + setAlignment( subControl, Qt::AlignLeft | Qt::AlignVCenter ); + + setFontRole( subControl, BodyLarge ); + + setColor( subControl | Q::Disabled, m_pal.onSurface38 ); + + if ( subControl == Q::Text ) + { + setColor( subControl, m_pal.onSurface ); + } + else + { + setColor( subControl | Q::Error, m_pal.error ); + setColor( subControl | Q::Error | Q::Hovered, m_pal.onSurface ); + } + } + + // Footer, CharacterCount + for ( const auto subControl : { Q::Footer, Q::CharacterCount } ) + { + setMargin( subControl, { 16_px, 4_px, 16_px, 4_px } ); + setFontRole( subControl, BodySmall ); + + setColor( subControl, m_pal.onSurfaceVariant ); + setColor( subControl | Q::Error, m_pal.error ); + setColor( subControl | Q::Disabled, m_pal.onSurface38 ); + } - setMargin( Q::Footer, { 16_px, 4_px, 16_px, 4_px } ); - setColor( Q::Footer, m_pal.onSurfaceVariant ); - setColor( Q::Footer | Q::Error, m_pal.error ); - setFontRole( Q::Footer, BodySmall ); setAlignment( Q::Footer, Qt::AlignLeft | Qt::AlignVCenter ); - - setColor( Q::Footer | Q::Disabled, m_pal.onSurface38 ); - - // CharacterCount - - setMargin( Q::CharacterCount, margin( Q::Footer ) ); - setColor( Q::CharacterCount, color( Q::Footer ) ); - setFontRole( Q::CharacterCount, fontRole( Q::Footer ) ); setAlignment( Q::CharacterCount, Qt::AlignRight | Qt::AlignVCenter ); - setColor( Q::CharacterCount | Q::Disabled, color( Q::Footer | Q::Disabled ) ); } void Editor::setupProgressBar() diff --git a/designsystems/material3/QskMaterial3TextFieldSkinlet.cpp b/designsystems/material3/QskMaterial3TextFieldSkinlet.cpp index 0cd3d3d1..696a4b5b 100644 --- a/designsystems/material3/QskMaterial3TextFieldSkinlet.cpp +++ b/designsystems/material3/QskMaterial3TextFieldSkinlet.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include @@ -19,25 +19,12 @@ namespace { const int spacingV = 0; // skin hint ! - QString effectiveHeaderText( const QskTextField* textField ) - { - if ( !textField->isEditing() && textField->text().isEmpty() ) - return QString(); - - return textField->headerText(); - } - inline bool hasCharacterCount( const QskTextField* textField ) { // magic number hardcoded in qquicktextinput.cpp return textField->maxLength() < 32767; } - inline bool hasBottomText( const QskTextField* textField ) - { - return !textField->footerText().isEmpty() || hasCharacterCount( textField ); - } - QString maxLengthString( const QskTextField* textField ) { QString s = QString::number( textField->text().length() ) @@ -79,7 +66,7 @@ namespace QskMaterial3TextFieldSkinlet::QskMaterial3TextFieldSkinlet( QskSkin* skin ) : Inherited( skin ) { - appendNodeRoles( { SupportingTextRole, CharacterCountRole } ); + appendNodeRoles( { CharacterCountRole } ); } QskMaterial3TextFieldSkinlet::~QskMaterial3TextFieldSkinlet() @@ -91,92 +78,47 @@ QRectF QskMaterial3TextFieldSkinlet::subControlRect( const QskSkinnable* skinnab { const auto textField = static_cast< const Q* >( skinnable ); - if ( subControl == Q::Panel ) - { - auto rect = contentsRect; - - if( textField->style() == QskTextField::OutlinedStyle ) - { - const auto h = textField->effectiveFontHeight( Q::Header ); - rect.setTop( rect.top() + 0.5 * h ); - } - - if( hasBottomText( textField ) ) - { - const auto margins = textField->marginHint( Q::Footer ); - - const auto h = textField->effectiveFontHeight( Q::Footer ) - + margins.top() + margins.bottom(); - - rect.setHeight( rect.height() - h ); - } - - return rect; - } - - if ( subControl == Q::Text ) - { - auto rect = Inherited::subControlRect( skinnable, contentsRect, Q::Text ); - - if ( !rect.isEmpty() && ( textField->style() == QskTextField::FilledStyle ) ) - { - const auto text = effectiveHeaderText( textField ); - if ( !text.isEmpty() ) - { - const auto h = skinnable->effectiveFontHeight( Q::Header ); - rect.translate( 0.0, 0.5 * ( h + spacingV ) ); - } - } - - return rect; - } - if ( subControl == Q::Header ) { - const auto text = effectiveHeaderText( textField ); + const auto text = effectiveText( textField, Q::Header ); if( text.isEmpty() ) return QRectF(); const QFontMetrics fm( textField->effectiveFont( Q::Header ) ); const auto textSize = fm.size( Qt::TextSingleLine | Qt::TextExpandTabs, text ); - qreal x, y; + if ( textField->style() == QskTextField::OutlinedStyle ) + { + const auto r = subControlRect( skinnable, contentsRect, Q::TextPanel ); - if ( textField->style() == QskTextField::FilledStyle ) + const auto x = r.left() + skinnable->paddingHint( Q::TextPanel ).left(); + const auto y = r.top() - 0.5 * textSize.height(); + + return QRectF( x, y, textSize.width(), textSize.height() ); + } + else { const auto r = subControlRect( skinnable, contentsRect, Q::Text ); - x = r.left(); - y = r.top() - spacingV - textSize.height(); + return QRectF( r.x(), r.top() - textSize.height(), + textSize.width(), textSize.height() ); } - else if ( textField->style() == QskTextField::OutlinedStyle ) - { - const auto r = subControlRect( skinnable, contentsRect, Q::Panel ); - - x = r.left() + skinnable->paddingHint( Q::Panel ).left(); - y = r.top() - 0.5 * textSize.height(); - } - - return QRectF( x, y, textSize.width(), textSize.height() ); } - if ( subControl == Q::Footer ) + if ( subControl == Q::TextPanel ) { - if( !textField->footerText().isEmpty() ) + auto rect = skinnable->subControlRect( contentsRect, Q::Panel ); + + if ( textField->style() == QskTextField::OutlinedStyle ) { - auto rect = contentsRect; - - const auto margins = textField->marginHint( subControl ); - const auto h = textField->effectiveFontHeight( subControl ) - + margins.top() + margins.bottom(); - - rect.setTop( rect.bottom() - h ); - rect.setLeft( rect.left() + margins.left() ); - - return rect; + const QFontMetrics fm( textField->effectiveFont( Q::Header ) ); + rect.setTop( rect.top() + 0.5 * fm.height() ); } - return QRectF(); + const auto h = skinnable->strutSizeHint( Q::TextPanel ).height(); + rect.setHeight( h ); + + return rect; } if ( subControl == Q::CharacterCount ) @@ -212,31 +154,23 @@ QSGNode* QskMaterial3TextFieldSkinlet::updateSubNode( switch ( nodeRole ) { - case PanelRole: + case TextPanelRole: { - if( ( textField->style() == QskTextField::OutlinedStyle ) && - !effectiveHeaderText( textField ).isEmpty() ) + const auto rect = textField->subControlRect( Q::TextPanel ); + + auto hints = textField->boxHints( Q::TextPanel ); + + if( textField->style() == QskTextField::OutlinedStyle ) { - auto clipRect = textField->subControlRect( Q::Header ); + const auto clipRect = textField->subControlRect( Q::Header ); if ( !clipRect.isEmpty() ) { - const auto subControl = Q::Panel; - - const auto panelRect = textField->subControlRect( subControl ); - - auto borderColors = textField->boxBorderColorsHint( subControl ); - borderColors = outlineColors( borderColors, panelRect, clipRect ); - - return updateBoxNode( skinnable, node, - panelRect, - skinnable->boxShapeHint( subControl ), - skinnable->boxBorderMetricsHint( subControl ), - borderColors, - skinnable->gradientHint( subControl ) ); + hints.borderColors = outlineColors( + hints.borderColors, rect, clipRect ); } } - return updateBoxNode( skinnable, node, Q::Panel ); + return updateBoxNode( skinnable, node, rect, hints ); } case CharacterCountRole: @@ -244,12 +178,6 @@ QSGNode* QskMaterial3TextFieldSkinlet::updateSubNode( return updateTextNode( skinnable, node, maxLengthString( textField ), Q::CharacterCount ); } - - case HeaderRole: - { - return updateTextNode( skinnable, node, - effectiveHeaderText( textField ), Q::Header ); - } } return Inherited::updateSubNode( skinnable, nodeRole, node ); @@ -258,40 +186,68 @@ QSGNode* QskMaterial3TextFieldSkinlet::updateSubNode( QSizeF QskMaterial3TextFieldSkinlet::sizeHint( const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& constraint ) const { + Q_UNUSED( constraint ); // TODO ... + if ( which != Qt::PreferredSize ) return QSizeF(); - auto hint = Inherited::sizeHint( skinnable, which, constraint ); - const auto textField = static_cast< const QskTextField* >( skinnable ); - if( textField->style() != QskTextField::PlainStyle ) - hint.rheight() += textField->effectiveFontHeight( Q::Header ) + spacingV; + auto hint = textField->unwrappedTextSize(); + hint = hint.expandedTo( skinnable->strutSizeHint( Q::TextPanel ) ); - if( hasBottomText( textField ) ) + if ( textField->style() == QskTextField::OutlinedStyle ) { - const auto margins = textField->marginHint( Q::Footer ); - hint.rheight() += textField->effectiveFontHeight( Q::Footer ) - + margins.top() + margins.bottom(); + const QFontMetrics fm( textField->effectiveFont( Q::Header ) ); + hint.rheight() += 0.5 * fm.height(); } + hint.rheight() += effectiveFooterHeight( textField ); + hint = hint.expandedTo( skinnable->strutSizeHint( Q::Panel ) ); + return hint; } -QString QskMaterial3TextFieldSkinlet::effectivePlaceholderText( - const QskTextField* textField ) const +QString QskMaterial3TextFieldSkinlet::effectiveText( + const QskTextField* textField, QskAspect::Subcontrol subControl ) const { - if ( textField->text().isEmpty() && - !( textField->isReadOnly() || textField->isEditing() ) ) - { - auto text = textField->placeholderText(); - if ( text.isEmpty() ) - text = textField->headerText(); + const bool showHeaderAsPlaceholder = + textField->text().isEmpty() && !textField->isEditing(); - return text; + if ( subControl == Q::Header ) + { + if ( showHeaderAsPlaceholder ) + return QString(); + } + else if ( subControl == Q::Placeholder ) + { + if ( showHeaderAsPlaceholder ) + { + auto text = Inherited::effectiveText( textField, Q::Placeholder ); + if ( text.isEmpty() ) + text = Inherited::effectiveText( textField, Q::Header ); + + return text; + } + + return QString(); } - return QString(); + return Inherited::effectiveText( textField, subControl ); +} + +qreal QskMaterial3TextFieldSkinlet::effectiveFooterHeight( + const QskTextField* textField ) const +{ + if ( hasCharacterCount( textField ) ) + { + const auto h = textField->effectiveFontHeight( Q::Footer ); + const auto margins = textField->marginHint( Q::Footer ); + + return h + margins.top() + margins.bottom(); + } + + return Inherited::effectiveFooterHeight( textField ); } #include "moc_QskMaterial3TextFieldSkinlet.cpp" diff --git a/designsystems/material3/QskMaterial3TextFieldSkinlet.h b/designsystems/material3/QskMaterial3TextFieldSkinlet.h index f9b35173..a47c2811 100644 --- a/designsystems/material3/QskMaterial3TextFieldSkinlet.h +++ b/designsystems/material3/QskMaterial3TextFieldSkinlet.h @@ -18,8 +18,7 @@ class QSK_MATERIAL3_EXPORT QskMaterial3TextFieldSkinlet : public QskTextFieldSki public: enum NodeRole : quint8 { - SupportingTextRole = Inherited::RoleCount, - CharacterCountRole + CharacterCountRole = Inherited::RoleCount }; Q_INVOKABLE QskMaterial3TextFieldSkinlet( QskSkin* = nullptr ); @@ -35,7 +34,10 @@ class QSK_MATERIAL3_EXPORT QskMaterial3TextFieldSkinlet : public QskTextFieldSki QSGNode* updateSubNode( const QskSkinnable*, quint8 nodeRole, QSGNode* ) const override; - QString effectivePlaceholderText( const QskTextField* ) const override; + QString effectiveText( const QskTextField*, + QskAspect::Subcontrol ) const override; + + qreal effectiveFooterHeight( const QskTextField* ) const override; }; #endif diff --git a/examples/gallery/inputs/InputPage.cpp b/examples/gallery/inputs/InputPage.cpp index d79366ee..20c88360 100644 --- a/examples/gallery/inputs/InputPage.cpp +++ b/examples/gallery/inputs/InputPage.cpp @@ -158,9 +158,8 @@ namespace StyleComboBox( QQuickItem* parent = nullptr ) : QskComboBox( parent ) { - addOption( QString(), "Plain" ); - addOption( QString(), "Outlined" ); addOption( QString(), "Filled" ); + addOption( QString(), "Outlined" ); } }; } diff --git a/playground/gradients/main.cpp b/playground/gradients/main.cpp index 3edd31ce..5f1c2677 100644 --- a/playground/gradients/main.cpp +++ b/playground/gradients/main.cpp @@ -51,12 +51,14 @@ namespace new QskTextLabel( label, this ); m_textField = new QskTextField( this ); + + m_textField->setText( "-0.000" ); + m_textField->setPreferredWidth( m_textField->sizeHint().width() ); + m_textField->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed ); + m_textField->setValidator( new InputValidator( m_textField ) ); m_textField->setText( QString::number( value ) ); - const QFontMetricsF fm( m_textField->font() ); - m_textField->setFixedWidth( fm.horizontalAdvance( "-0.000" ) ); - connect( m_textField, &QskTextField::editingChanged, this, [ this ]( bool on ) { if ( !on ) Q_EMIT valueChanged(); } ); diff --git a/src/controls/QskTextField.cpp b/src/controls/QskTextField.cpp index 23355ab5..ddc08d1a 100644 --- a/src/controls/QskTextField.cpp +++ b/src/controls/QskTextField.cpp @@ -29,7 +29,7 @@ class QskTextField::PrivateData QString footerText; QString placeholderText; - Style style = PlainStyle; + Style style = FilledStyle; QskAspect::States buttonStates; }; @@ -60,6 +60,7 @@ void QskTextField::setStyle( Style style ) m_data->style = style; resetImplicitSize(); + polish(); update(); Q_EMIT styleChanged( style ); diff --git a/src/controls/QskTextField.h b/src/controls/QskTextField.h index 139c61a8..67968817 100644 --- a/src/controls/QskTextField.h +++ b/src/controls/QskTextField.h @@ -35,10 +35,8 @@ class QSK_EXPORT QskTextField : public QskTextInput enum Style : quint8 { - PlainStyle, - - OutlinedStyle, - FilledStyle + FilledStyle, + OutlinedStyle }; Q_ENUM( Style ) diff --git a/src/controls/QskTextFieldSkinlet.cpp b/src/controls/QskTextFieldSkinlet.cpp index 7b1e4e70..955bf51a 100644 --- a/src/controls/QskTextFieldSkinlet.cpp +++ b/src/controls/QskTextFieldSkinlet.cpp @@ -5,16 +5,27 @@ #include "QskTextFieldSkinlet.h" #include "QskTextField.h" - -#include +#include "QskFunctions.h" using Q = QskTextField; +static qreal qskEffectiveTextHeight( const QskTextField* textField, + QskAspect::Subcontrol subControl ) +{ + auto h = textField->effectiveFontHeight( subControl ); + + const auto margins = textField->marginHint( subControl ); + h += margins.top() + margins.bottom(); + + const auto sz = textField->strutSizeHint( subControl ); + + return qMax( h, sz.height() ); +} + QskTextFieldSkinlet::QskTextFieldSkinlet( QskSkin* skin ) : Inherited( skin ) { - setNodeRoles( { PanelRole, TextPanelRole, - IconRole, ButtonPanelRole, ButtonRole, + setNodeRoles( { TextPanelRole, IconRole, ButtonPanelRole, ButtonRole, PlaceholderRole, HeaderRole, FooterRole } ); } @@ -25,41 +36,68 @@ QskTextFieldSkinlet::~QskTextFieldSkinlet() QRectF QskTextFieldSkinlet::subControlRect( const QskSkinnable* skinnable, const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const { + const auto textField = static_cast< const QskTextField* >( skinnable ); + if ( subControl == Q::Panel ) + { return contentsRect; + } + + if ( subControl == Q::Header ) + { + if ( auto h = effectiveHeaderHeight( textField ) ) + { + const auto m = textField->marginHint( Q::Header ); + const auto r = subControlRect( skinnable, contentsRect, Q::TextPanel ); + + return QRectF( r.left() + m.left(), + r.top() - m.bottom() - h, r.width() - m.left() - m.right(), h ); + } + + return QRectF(); + } + + if ( subControl == Q::Footer ) + { + if ( const auto h = effectiveFooterHeight( textField ) ) + { + const auto m = textField->marginHint( Q::Footer ); + const auto r = subControlRect( skinnable, contentsRect, Q::TextPanel ); + + return QRectF( r.left() + m.left(), + r.bottom() + m.top(), r.width() - m.left() - m.right(), h ); + } + + return QRectF(); + } if ( subControl == Q::TextPanel ) - return skinnable->subControlContentsRect( contentsRect, Q::Panel ); + { + const auto rect = textField->subControlContentsRect( contentsRect, Q::Panel ); + return inputPanelRect( textField, rect ); + } if ( subControl == Q::Text ) { - auto rect = skinnable->subControlContentsRect( contentsRect, Q::TextPanel ); + auto rect = textField->subControlContentsRect( contentsRect, Q::TextPanel ); - if( !skinnable->symbolHint( Q::Icon ).isEmpty() ) - { - const auto r = subControlRect( skinnable, contentsRect, Q::Icon ); - if ( !r.isEmpty() ) - rect.setLeft( r.right() ); - } + const auto iconRect = subControlRect( skinnable, contentsRect, Q::Icon ); + if ( !iconRect.isEmpty() ) + rect.setLeft( iconRect.right() ); - if( !skinnable->symbolHint( Q::Button ).isEmpty() ) - { - const auto r = subControlRect( skinnable, contentsRect, Q::Button ); - if( !r.isEmpty() ) - rect.setRight( r.left() ); - } + const auto buttonRect = subControlRect( skinnable, contentsRect, Q::Button ); + if( !buttonRect.isEmpty() ) + rect.setRight( buttonRect.left() ); const auto h = skinnable->effectiveFontHeight( Q::Text ); rect.setTop( rect.center().y() - 0.5 * h ); rect.setHeight( h ); - rect = rect.marginsAdded( skinnable->marginHint( Q::Text ) ); return rect; } if ( subControl == Q::Placeholder ) { - const auto textField = static_cast< const QskTextField* >( skinnable ); if( textField->text().isEmpty() ) return subControlRect( skinnable, contentsRect, Q::Text ); @@ -70,15 +108,10 @@ QRectF QskTextFieldSkinlet::subControlRect( const QskSkinnable* skinnable, { if( !skinnable->symbolHint( subControl ).isEmpty() ) { - const auto panelRect = skinnable->subControlContentsRect( - contentsRect, Q::TextPanel ); + const auto rect = textField->subControlContentsRect( contentsRect, Q::TextPanel ); - auto rect = panelRect; - - rect.setSize( skinnable->strutSizeHint( subControl ) ); - rect.moveCenter( { rect.center().x(), panelRect.center().y() } ); - - return rect; + return qskAlignedRectF( rect, + skinnable->strutSizeHint( Q::Icon ), Qt::AlignLeft | Qt::AlignVCenter ); } return QRectF(); @@ -86,15 +119,18 @@ QRectF QskTextFieldSkinlet::subControlRect( const QskSkinnable* skinnable, if ( subControl == Q::ButtonPanel ) { - const auto textField = static_cast< const QskTextField* >( skinnable ); if ( textField->buttonStates() & Q::Hovered ) { - const auto r = subControlRect( skinnable, contentsRect, Q::Button ); + const auto sz = skinnable->strutSizeHint( Q::ButtonPanel ); + if ( !sz.isEmpty() ) + { + const auto r = subControlRect( skinnable, contentsRect, Q::Button ); - QRectF rect( QPointF(), skinnable->strutSizeHint( subControl ) ); - rect.moveCenter( r.center() ); + QRectF rect( QPointF(), sz ); + rect.moveCenter( r.center() ); - return rect; + return rect; + } } return QRectF(); @@ -102,19 +138,12 @@ QRectF QskTextFieldSkinlet::subControlRect( const QskSkinnable* skinnable, if ( subControl == Q::Button ) { - if( !skinnable->symbolHint( subControl ).isEmpty() ) + if( !skinnable->symbolHint( Q::Button ).isEmpty() ) { - const auto panelRect = skinnable->subControlContentsRect( - contentsRect, Q::TextPanel ); + const auto rect = textField->subControlContentsRect( contentsRect, Q::TextPanel ); - auto rect = panelRect; - - const auto size = skinnable->strutSizeHint( subControl ); - rect.setHeight( size.height() ); - rect.moveCenter( { rect.center().x(), panelRect.center().y() } ); - rect.setLeft( rect.right() - size.width() ); - - return rect; + return qskAlignedRectF( rect, + skinnable->strutSizeHint( Q::Icon ), Qt::AlignRight | Qt::AlignVCenter ); } return QRectF(); @@ -130,22 +159,18 @@ QSGNode* QskTextFieldSkinlet::updateSubNode( switch ( nodeRole ) { - case PanelRole: - { - return updateBoxNode( skinnable, node, Q::Panel ); - } case TextPanelRole: { return updateBoxNode( skinnable, node, Q::TextPanel ); } case PlaceholderRole: { - const auto text = effectivePlaceholderText( textField ); + const auto subControl = Q::Placeholder; + + const auto text = effectiveText( textField, subControl ); if ( text.isEmpty() ) return nullptr; - const auto subControl = Q::Placeholder; - QskSkinHintStatus status; auto options = skinnable->textOptionsHint( subControl, &status ); @@ -161,13 +186,13 @@ QSGNode* QskTextFieldSkinlet::updateSubNode( case HeaderRole: { return updateTextNode( skinnable, node, - textField->headerText(), Q::Header ); + effectiveText( textField, Q::Header ), Q::Header ); } case FooterRole: { - return updateTextNode( skinnable, node, - textField->footerText(), Q::Footer ); + return updateTextNode( skinnable, node, + effectiveText( textField, Q::Footer ), Q::Footer ); } case IconRole: @@ -183,6 +208,37 @@ QSGNode* QskTextFieldSkinlet::updateSubNode( return Inherited::updateSubNode( skinnable, nodeRole, node ); } +QRectF QskTextFieldSkinlet::inputPanelRect( + const QskTextField* textField, const QRectF& rect ) const +{ + qreal h = textField->effectiveFontHeight( Q::Text ); + h = textField->outerBoxSize( Q::TextPanel, QSizeF( rect.width(), h ) ).height(); + h = qMax( h, textField->strutSizeHint( Q::TextPanel ).height() ); + + /* + when having textfields in horizontal layouts you usually want + the text panels being vertically aligned - regardless of having + Q::Header/Q::Footer being available. + */ + + auto top = qskEffectiveTextHeight( textField, Q::Header ); + auto bottom = qskEffectiveTextHeight( textField, Q::Footer ); + + if ( rect.height() < top + h + bottom ) + { + if ( effectiveText( textField, Q::Footer ).isEmpty() ) + bottom = 0.0; + } + + if ( rect.height() < top + h + bottom ) + { + if ( effectiveText( textField, Q::Header ).isEmpty() ) + top = 0.0; + } + + return QRectF( rect.left(), rect.top() + top, rect.width(), h ); +} + QSizeF QskTextFieldSkinlet::sizeHint( const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& constraint ) const { @@ -191,13 +247,22 @@ QSizeF QskTextFieldSkinlet::sizeHint( const QskSkinnable* skinnable, if ( which != Qt::PreferredSize ) return QSizeF(); - const auto input = static_cast< const QskAbstractTextInput* >( skinnable ); + const auto textField = static_cast< const QskTextField* >( skinnable ); - auto hint = input->unwrappedTextSize(); - hint = hint.grownBy( skinnable->marginHint( Q::Text ) ); + auto hint = textField->unwrappedTextSize(); - hint = input->outerBoxSize( Q::TextPanel, hint ); - hint = hint.expandedTo( input->strutSizeHint( Q::TextPanel ) ); + if( !skinnable->symbolHint( Q::Button ).isEmpty() ) + { + const auto sz = skinnable->strutSizeHint( Q::Button ); + if ( sz.width() > 0.0 ) + hint.rwidth() += sz.width(); + } + + hint = skinnable->outerBoxSize( Q::TextPanel, hint ); + hint = hint.expandedTo( skinnable->strutSizeHint( Q::TextPanel ) ); + + hint.rheight() += effectiveHeaderHeight( textField ); + hint.rheight() += effectiveFooterHeight( textField ); hint = skinnable->outerBoxSize( Q::Panel, hint ); hint = hint.expandedTo( skinnable->strutSizeHint( Q::Panel ) ); @@ -205,16 +270,42 @@ QSizeF QskTextFieldSkinlet::sizeHint( const QskSkinnable* skinnable, return hint; } -QString QskTextFieldSkinlet::effectivePlaceholderText( - const QskTextField* textField ) const +QString QskTextFieldSkinlet::effectiveText( + const QskTextField* textField, QskAspect::Subcontrol subcontrol ) const { - if ( textField->text().isEmpty() && - !( textField->isReadOnly() || textField->isEditing() ) ) + if ( subcontrol == Q::Text ) + return textField->text(); + + if ( subcontrol == Q::Placeholder ) { - return textField->placeholderText(); + if ( textField->text().isEmpty() && + !( textField->isReadOnly() || textField->isEditing() ) ) + { + return textField->placeholderText(); + } + + return QString(); } + if ( subcontrol == Q::Header ) + return textField->headerText(); + + if ( subcontrol == Q::Footer ) + return textField->footerText(); + return QString(); } +qreal QskTextFieldSkinlet::effectiveHeaderHeight( const QskTextField* textField ) const +{ + const auto text = effectiveText( textField, Q::Header ); + return text.isEmpty() ? 0.0 : qskEffectiveTextHeight( textField, Q::Header ); +} + +qreal QskTextFieldSkinlet::effectiveFooterHeight( const QskTextField* textField ) const +{ + const auto text = effectiveText( textField, Q::Footer ); + return text.isEmpty() ? 0.0 : qskEffectiveTextHeight( textField, Q::Footer ); +} + #include "moc_QskTextFieldSkinlet.cpp" diff --git a/src/controls/QskTextFieldSkinlet.h b/src/controls/QskTextFieldSkinlet.h index 14380363..bed44868 100644 --- a/src/controls/QskTextFieldSkinlet.h +++ b/src/controls/QskTextFieldSkinlet.h @@ -19,7 +19,6 @@ class QSK_EXPORT QskTextFieldSkinlet : public QskSkinlet public: enum NodeRole : quint8 { - PanelRole, TextPanelRole, HeaderRole, @@ -45,7 +44,14 @@ class QSK_EXPORT QskTextFieldSkinlet : public QskSkinlet QSGNode* updateSubNode( const QskSkinnable*, quint8 nodeRole, QSGNode* ) const override; - virtual QString effectivePlaceholderText( const QskTextField* ) const; + virtual QString effectiveText( const QskTextField*, + QskAspect::Subcontrol ) const; + + qreal effectiveHeaderHeight( const QskTextField* ) const; + virtual qreal effectiveFooterHeight( const QskTextField* ) const; + + private: + QRectF inputPanelRect( const QskTextField*, const QRectF& ) const; }; #endif diff --git a/src/controls/QskTextInput.cpp b/src/controls/QskTextInput.cpp index 64190e8a..c05f65cf 100644 --- a/src/controls/QskTextInput.cpp +++ b/src/controls/QskTextInput.cpp @@ -161,7 +161,7 @@ QskTextInput::QskTextInput( QQuickItem* parent ) setAcceptedMouseButtons( wrappedInput->acceptedMouseButtons() ); wrappedInput->setAcceptedMouseButtons( Qt::NoButton ); - initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Fixed ); + initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Minimum ); setup( wrappedInput );