diff --git a/designsystems/fluent2/QskFluent2Icons.qrc b/designsystems/fluent2/QskFluent2Icons.qrc
index 7587fb4c..0d9b967d 100644
--- a/designsystems/fluent2/QskFluent2Icons.qrc
+++ b/designsystems/fluent2/QskFluent2Icons.qrc
@@ -3,5 +3,7 @@
icons/qvg/checkmark.qvg
icons/qvg/chevron_down.qvg
icons/qvg/chevron_up.qvg
+ icons/qvg/dismiss.qvg
+ icons/qvg/search.qvg
diff --git a/designsystems/fluent2/QskFluent2Skin.cpp b/designsystems/fluent2/QskFluent2Skin.cpp
index e38340ce..127bb3b9 100644
--- a/designsystems/fluent2/QskFluent2Skin.cpp
+++ b/designsystems/fluent2/QskFluent2Skin.cpp
@@ -1873,6 +1873,22 @@ void Editor::setupTextFieldMetrics()
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 );
+
+
+ setSymbol( Q::Icon, symbol( "search" ) );
+ setSymbol( Q::Button, symbol( "dismiss" ) );
+
+ for ( const auto subControl : { Q::Icon, Q::Button } )
+ {
+ setMargin( subControl, 2_px );
+ setStrutSize( subControl, 16_px, 16_px );
+ }
}
void Editor::setupTextFieldColors(
@@ -1880,54 +1896,66 @@ void Editor::setupTextFieldColors(
{
using Q = QskTextField;
using A = QskAspect;
+ using W = QskFluent2Skin;
const auto& pal = theme.palette;
- const auto text = Q::Text | section;
-
-#if 1
- setColor( text, pal.fillColor.text.primary );
- setColor( text | Q::Selected, pal.fillColor.textOnAccent.selectedText );
- setColor( text | Q::Disabled, pal.fillColor.text.disabled );
-#endif
+ setColor( Q::Text | section | Q::Selected, pal.fillColor.textOnAccent.selectedText );
+ setColor( Q::TextPanel | section | Q::Selected, pal.fillColor.accent.selectedTextBackground );
- setColor( Q::TextPanel | Q::Selected, pal.fillColor.accent.selectedTextBackground );
- setColor( Q::Placeholder, pal.fillColor.text.secondary );
+ setColor( Q::Placeholder | section, pal.fillColor.text.secondary );
+
+ setColor( Q::Header | section, pal.fillColor.text.primary );
for( const auto state : { A::NoState, Q::Hovered, Q::Focused, Q::Editing, Q::Disabled } )
{
- QRgb panelColor, borderColor1, borderColor2;
+ QRgb panelColor, borderColor1, borderColor2, textColor;
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.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.primary;
}
else if ( state == Q::Disabled )
{
panelColor = pal.fillColor.control.disabled;
borderColor1 = borderColor2 = pal.strokeColor.control.defaultColor;
+ textColor = pal.fillColor.text.disabled;
}
else // A::NoState
{
panelColor = pal.fillColor.control.defaultColor;
borderColor1 = pal.elevation.textControl.border[0];
borderColor2 = pal.elevation.textControl.border[1];
+ textColor = pal.fillColor.text.primary;
}
const auto panel = Q::TextPanel | section | state;
+ const auto text = Q::Text | section | state;
panelColor = rgbSolid( panelColor, pal.background.solid.base );
setGradient( panel, panelColor );
setBoxBorderGradient( panel, borderColor1, borderColor2, panelColor );
+
+ setColor( text, textColor );
+ }
+
+ for ( const auto subControl : { Q::Icon, Q::Button } )
+ {
+ const auto aspect = subControl | section;
+
+ setGraphicRole( aspect, W::GraphicRoleFillColorTextSecondary );
+ setGraphicRole( aspect | Q::Disabled, W::GraphicRoleFillColorTextDisabled );
}
}
diff --git a/designsystems/fluent2/QskFluent2TextFieldSkinlet.cpp b/designsystems/fluent2/QskFluent2TextFieldSkinlet.cpp
index 7c4c9b9b..f05cb44e 100644
--- a/designsystems/fluent2/QskFluent2TextFieldSkinlet.cpp
+++ b/designsystems/fluent2/QskFluent2TextFieldSkinlet.cpp
@@ -20,13 +20,41 @@ 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
{
- return Inherited::sizeHint( skinnable, which, constraint );
+ 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/icons/dismiss.svg b/designsystems/fluent2/icons/dismiss.svg
new file mode 100644
index 00000000..ea678912
--- /dev/null
+++ b/designsystems/fluent2/icons/dismiss.svg
@@ -0,0 +1,3 @@
+
diff --git a/designsystems/fluent2/icons/qvg/dismiss.qvg b/designsystems/fluent2/icons/qvg/dismiss.qvg
new file mode 100644
index 00000000..0cce0099
Binary files /dev/null and b/designsystems/fluent2/icons/qvg/dismiss.qvg differ
diff --git a/designsystems/fluent2/icons/qvg/search.qvg b/designsystems/fluent2/icons/qvg/search.qvg
new file mode 100644
index 00000000..48e33a09
Binary files /dev/null and b/designsystems/fluent2/icons/qvg/search.qvg differ
diff --git a/designsystems/fluent2/icons/search.svg b/designsystems/fluent2/icons/search.svg
new file mode 100644
index 00000000..ebdd5c29
--- /dev/null
+++ b/designsystems/fluent2/icons/search.svg
@@ -0,0 +1,3 @@
+
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 c4b78be1..5d2c5ef5 100644
--- a/designsystems/material3/QskMaterial3Skin.cpp
+++ b/designsystems/material3/QskMaterial3Skin.cpp
@@ -478,43 +478,161 @@ void Editor::setupTextArea()
void Editor::setupTextField()
{
using Q = QskTextField;
+ using M3 = QskMaterial3Skin;
+ using A = QskAspect;
setColor( Q::Text, m_pal.onSurface );
setFontRole( Q::Text, BodyLarge );
setColor( Q::Text | Q::Disabled, m_pal.onSurface38 );
- setStrutSize( Q::Panel, -1.0, 56_px );
- setPadding( Q::Panel, { 12_px, 8_px, 12_px, 8_px } );
- setGradient( Q::Panel, m_pal.surfaceVariant );
- setColor( Q::TextPanel | Q::Selected, m_pal.primary12 );
- setBoxShape( Q::Panel, m_pal.shapeExtraSmallTop );
- setBoxBorderMetrics( Q::Panel, { 0, 0, 0, 1_px } );
- setBoxBorderColors( Q::Panel, m_pal.onSurfaceVariant );
- setSpacing( Q::Panel, 8_px );
+ const auto Outlined = static_cast< A::Variation >( Q::OutlinedStyle );
+ const auto Filled = static_cast< A::Variation >( Q::FilledStyle );
- const auto hoverColor = flattenedColor( m_pal.onSurfaceVariant,
- m_pal.surfaceVariant, m_pal.hoverOpacity );
- setGradient( Q::Panel | Q::Hovered, hoverColor );
+ const auto activeStates = Q::Focused | Q::Editing;
- const auto focusColor = flattenedColor( m_pal.onSurfaceVariant,
- m_pal.surfaceVariant, m_pal.focusOpacity );
- setGradient( Q::Panel | Q::Focused, focusColor );
+ {
+ // Text
- // ### Also add a pressed state
+ setAnimation( Q::TextPanel | A::Color, qskDuration );
+ setAnimation( Q::TextPanel | A::Metric, qskDuration );
+ }
- setFontRole( Q::Text, BodyLarge );
+ for ( const auto variation : { A::NoVariation, Filled, Outlined } )
+ {
+ const auto Panel = Q::Panel | variation;
- setAlignment( Q::Placeholder, Qt::AlignLeft | Qt::AlignVCenter );
+ QskBoxBorderMetrics borderMetrics[2];
- const auto disabledPanelColor = QskRgb::toTransparentF( m_pal.onSurface, 0.04 );
- setGradient( Q::Panel | Q::Disabled, disabledPanelColor );
- setBoxBorderColors( Q::Panel | Q::Disabled, m_pal.onSurface38 );
+ if ( variation == Filled )
+ {
+ setBoxShape( Panel, m_pal.shapeExtraSmallTop );
- // PlaceholderText
+ borderMetrics[0].setBottom( 1 );
+ borderMetrics[1].setBottom( 2 );
- setColor( Q::Placeholder, color( Q::Text ) );
- setFontRole( Q::Placeholder, BodyLarge );
- setAlignment( Q::Placeholder, Qt::AlignLeft | Qt::AlignVCenter );
+ 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 );
+ }
+
+ // Icon
+
+ setStrutSize( Q::Icon, { 24_px, 24_px } );
+ setMargin( Q::Icon, 2_px );
+ setSymbol( Q::Icon, symbol( "text_field_search" ) );
+
+ setGraphicRole( Q::Icon, M3::GraphicRoleOnSurface );
+ setGraphicRole( Q::Icon | Q::Error, M3::GraphicRoleOnSurfaceVariant );
+
+ 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 } );
+ setMargin( Q::Button, 2_px );
+ setGraphicRole( Q::Button, M3::GraphicRoleOnSurfaceVariant );
+ setSymbol( Q::Button, symbol( "text_field_cancel" ) );
+
+ setSymbol( Q::Button | Q::Error, symbol( "text_field_error" ) );
+ setGraphicRole( Q::Button | Q::Error, M3::GraphicRoleError );
+ setGraphicRole( Q::Button | Q::Error | Q::Hovered, M3::GraphicRoleOnErrorContainer );
+
+ setGraphicRole( Q::Button | Q::Disabled, M3::GraphicRoleOnSurface38 );
+
+ // ButtonPanel
+
+ setStrutSize( Q::ButtonPanel, { 45_px, 45_px } );
+ setGradient( Q::ButtonPanel | Q::Hovered, m_pal.onSurface8 );
+ setBoxShape( Q::ButtonPanel, 100, Qt::RelativeSize );
+
+
+ // SupportingText
+
+ 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()
@@ -1619,6 +1737,7 @@ QskMaterial3Theme::QskMaterial3Theme( QskSkin::ColorScheme colorScheme,
elevation2 = QskShadowMetrics( -2, 8, { 0, 2 } );
elevation3 = QskShadowMetrics( -1, 11, { 0, 2 } );
+ shapeExtraSmall = QskBoxShapeMetrics( 4_px, 4_px, 4_px, 4_px );
shapeExtraSmallTop = QskBoxShapeMetrics( 4_px, 4_px, 0, 0 );
}
@@ -1745,6 +1864,7 @@ void QskMaterial3Skin::setupGraphicFilters( const QskMaterial3Theme& theme )
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 a1063d65..8042dda5 100644
--- a/designsystems/material3/QskMaterial3Skin.h
+++ b/designsystems/material3/QskMaterial3Skin.h
@@ -107,6 +107,7 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Theme
qreal stateOpacity( int state ) const;
+ QskBoxShapeMetrics shapeExtraSmall;
QskBoxShapeMetrics shapeExtraSmallTop;
};
@@ -119,7 +120,9 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Skin : public QskSkin
public:
enum GraphicRole
{
+ GraphicRoleError,
GraphicRoleOnError,
+ GraphicRoleOnErrorContainer,
GraphicRoleOnPrimary,
GraphicRoleOnPrimaryContainer,
GraphicRoleOnSecondaryContainer,
diff --git a/designsystems/material3/QskMaterial3TextFieldSkinlet.cpp b/designsystems/material3/QskMaterial3TextFieldSkinlet.cpp
index fb0882a8..0cd3d3d1 100644
--- a/designsystems/material3/QskMaterial3TextFieldSkinlet.cpp
+++ b/designsystems/material3/QskMaterial3TextFieldSkinlet.cpp
@@ -4,11 +4,82 @@
*****************************************************************************/
#include "QskMaterial3TextFieldSkinlet.h"
-#include "QskTextField.h"
+#include "QskMaterial3Skin.h"
+
+#include
+#include
+#include
+#include
+
+#include
+
+using Q = QskTextField;
+
+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() )
+ + " / " + QString::number( textField->maxLength() );
+ return s;
+ }
+
+ // We need to "cut a hole" in the upper gradient for the label text:
+ QskBoxBorderColors outlineColors( const QskBoxBorderColors& colors,
+ const QRectF& rect, const QRectF& clipRect )
+ {
+ auto gradient = colors.gradientAt( Qt::TopEdge );
+
+ const auto margin = 6; // ->skin
+ auto s1 = ( clipRect.left() - margin - rect.left() ) / rect.width();
+ auto s2 = ( clipRect.right() - rect.left() ) / rect.width();
+
+ s1 = qBound( 0.0, s1, 1.0 );
+ s2 = qBound( 0.0, s2, 1.0 );
+
+ // not correct, when gradient is not monochrome !!!
+
+ gradient.setStops( {
+ { 0.0, gradient.startColor() },
+ { s1, gradient.startColor() },
+ { s1, Qt::transparent },
+ { s2, Qt::transparent },
+ { s2, gradient.endColor() },
+ { 1.0, gradient.endColor() }
+ } );
+
+ auto borderColors = colors;
+ borderColors.setGradientAt( Qt::TopEdge, gradient );
+
+ return borderColors;
+ }
+}
QskMaterial3TextFieldSkinlet::QskMaterial3TextFieldSkinlet( QskSkin* skin )
: Inherited( skin )
{
+ appendNodeRoles( { SupportingTextRole, CharacterCountRole } );
}
QskMaterial3TextFieldSkinlet::~QskMaterial3TextFieldSkinlet()
@@ -18,19 +89,209 @@ QskMaterial3TextFieldSkinlet::~QskMaterial3TextFieldSkinlet()
QRectF QskMaterial3TextFieldSkinlet::subControlRect( const QskSkinnable* skinnable,
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
{
+ 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 );
+ 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::FilledStyle )
+ {
+ const auto r = subControlRect( skinnable, contentsRect, Q::Text );
+
+ x = r.left();
+ y = r.top() - spacingV - 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( !textField->footerText().isEmpty() )
+ {
+ 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;
+ }
+
+ return QRectF();
+ }
+
+ if ( subControl == Q::CharacterCount )
+ {
+ if( hasCharacterCount( textField ) )
+ {
+ auto rect = contentsRect;
+
+ const auto margins = textField->marginHint( subControl );
+ const auto h = textField->effectiveFontHeight( subControl )
+ + margins.top() + margins.bottom();
+
+ rect.setTop( rect.bottom() - h );
+
+ const QFontMetricsF fm( textField->effectiveFont( subControl ) );
+ const auto w = qskHorizontalAdvance( fm, maxLengthString( textField ) );
+ rect.setRight( rect.right() - margins.right() );
+ rect.setLeft( rect.right() - ( margins.left() + w + margins.right() ) );
+
+ return rect;
+ }
+
+ return QRectF();
+ }
+
return Inherited::subControlRect( skinnable, contentsRect, subControl );
}
QSGNode* QskMaterial3TextFieldSkinlet::updateSubNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
{
+ const auto textField = static_cast< const Q* >( skinnable );
+
+ switch ( nodeRole )
+ {
+ case PanelRole:
+ {
+ if( ( textField->style() == QskTextField::OutlinedStyle ) &&
+ !effectiveHeaderText( textField ).isEmpty() )
+ {
+ 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 ) );
+ }
+ }
+
+ return updateBoxNode( skinnable, node, Q::Panel );
+ }
+
+ case CharacterCountRole:
+ {
+ return updateTextNode( skinnable, node,
+ maxLengthString( textField ), Q::CharacterCount );
+ }
+
+ case HeaderRole:
+ {
+ return updateTextNode( skinnable, node,
+ effectiveHeaderText( textField ), Q::Header );
+ }
+ }
+
return Inherited::updateSubNode( skinnable, nodeRole, node );
}
QSizeF QskMaterial3TextFieldSkinlet::sizeHint( const QskSkinnable* skinnable,
Qt::SizeHint which, const QSizeF& constraint ) const
{
- return Inherited::sizeHint( skinnable, which, constraint );
+ 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;
+
+ if( hasBottomText( textField ) )
+ {
+ const auto margins = textField->marginHint( Q::Footer );
+ hint.rheight() += textField->effectiveFontHeight( Q::Footer )
+ + margins.top() + margins.bottom();
+ }
+
+ return hint;
+}
+
+QString QskMaterial3TextFieldSkinlet::effectivePlaceholderText(
+ const QskTextField* textField ) const
+{
+ if ( textField->text().isEmpty() &&
+ !( textField->isReadOnly() || textField->isEditing() ) )
+ {
+ auto text = textField->placeholderText();
+ if ( text.isEmpty() )
+ text = textField->headerText();
+
+ return text;
+ }
+
+ return QString();
}
#include "moc_QskMaterial3TextFieldSkinlet.cpp"
diff --git a/designsystems/material3/QskMaterial3TextFieldSkinlet.h b/designsystems/material3/QskMaterial3TextFieldSkinlet.h
index 420d69fb..f9b35173 100644
--- a/designsystems/material3/QskMaterial3TextFieldSkinlet.h
+++ b/designsystems/material3/QskMaterial3TextFieldSkinlet.h
@@ -16,6 +16,12 @@ class QSK_MATERIAL3_EXPORT QskMaterial3TextFieldSkinlet : public QskTextFieldSki
using Inherited = QskTextFieldSkinlet;
public:
+ enum NodeRole : quint8
+ {
+ SupportingTextRole = Inherited::RoleCount,
+ CharacterCountRole
+ };
+
Q_INVOKABLE QskMaterial3TextFieldSkinlet( QskSkin* = nullptr );
~QskMaterial3TextFieldSkinlet() override;
@@ -28,6 +34,8 @@ class QSK_MATERIAL3_EXPORT QskMaterial3TextFieldSkinlet : public QskTextFieldSki
protected:
QSGNode* updateSubNode( const QskSkinnable*,
quint8 nodeRole, QSGNode* ) const override;
+
+ QString effectivePlaceholderText( const QskTextField* ) const override;
};
#endif
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 00000000..7607d209
Binary files /dev/null and b/designsystems/material3/icons/qvg/text_field_cancel.qvg differ
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 00000000..013508ad
Binary files /dev/null and b/designsystems/material3/icons/qvg/text_field_error.qvg differ
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 00000000..b289e352
Binary files /dev/null and b/designsystems/material3/icons/qvg/text_field_search.qvg differ
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 7cf72289..d79366ee 100644
--- a/examples/gallery/inputs/InputPage.cpp
+++ b/examples/gallery/inputs/InputPage.cpp
@@ -10,6 +10,12 @@
#include
#include
#include
+#include
+#include
+#include
+#include
+
+#include
namespace
{
@@ -66,37 +72,62 @@ namespace
{
public:
TextInputBox( QQuickItem* parent = nullptr )
- : QskLinearBox( Qt::Horizontal, parent )
+ : QskLinearBox( Qt::Horizontal, 3, parent )
{
setSpacing( 20 );
+ setDefaultAlignment( Qt::AlignHCenter | Qt::AlignTop );
{
- {
- auto field = new QskTextField( this );
- field->setText( "John Doe" );
- field->setPlaceholderText( "" );
-
-#if 0
- connect( field, &QskTextField::textChanged,
- [field]() { qDebug() << "Text:" << field->text(); } );
-#endif
-
- }
-
- {
- auto field = new QskTextField( this );
- field->setReadOnly( true );
- field->setText( "Read Only" );
- field->setSizePolicy( Qt::Horizontal, QskSizePolicy::MinimumExpanding );
- }
-
- {
- auto field = new QskTextField( this );
- field->setMaxLength( 5 );
- field->setEchoMode( QskTextField::Password );
- field->setPlaceholderText( "" );
- }
+ auto field = new QskTextField( this );
+ field->setHeaderText( "Name" );
+ field->setText( "John Doe" );
+ field->setPlaceholderText( "" );
+ field->setFooterText( "Required *" );
}
+
+ {
+ auto field = new QskTextField( this );
+ field->setHeaderText( "Nickname" );
+ field->setPlaceholderText( "" );
+ field->setFooterText( "Optional" );
+ }
+ {
+ auto field = new QskTextField( this );
+ field->setIcon( {} );
+ field->setPlaceholderText( "" );
+ }
+
+ {
+ auto field = new QskTextField( this );
+ field->setSkinStateFlag( QskTextField::Error );
+ field->setText( "Error Text" );
+ field->setHeaderText( "error" );
+ field->setPlaceholderText( "" );
+ field->setFooterText( "error text" );
+ }
+
+ {
+ auto field = new QskTextField( this );
+ field->setReadOnly( true );
+ field->setText( "Read Only" );
+ field->setHeaderText( "read only" );
+ field->setSizePolicy( Qt::Horizontal, QskSizePolicy::MinimumExpanding );
+ }
+
+ {
+ auto field = new QskTextField( this );
+ field->setMaxLength( 15 );
+ field->setHeaderText( "password" );
+ field->setEchoMode( QskTextField::Password );
+ field->setPlaceholderText( "" );
+ }
+ }
+
+ void setStyle( int style )
+ {
+ auto textFields = findChildren< QskTextField* >();
+ for ( auto field : textFields )
+ field->setStyle( static_cast< QskTextField::Style >( style ) );
}
};
@@ -120,6 +151,18 @@ namespace
}
}
};
+
+ class StyleComboBox : public QskComboBox
+ {
+ public:
+ StyleComboBox( QQuickItem* parent = nullptr )
+ : QskComboBox( parent )
+ {
+ addOption( QString(), "Plain" );
+ addOption( QString(), "Outlined" );
+ addOption( QString(), "Filled" );
+ }
+ };
}
InputPage::InputPage( QQuickItem* parent )
@@ -146,30 +189,44 @@ InputPage::InputPage( QQuickItem* parent )
}
auto spinBox = new QskSpinBox( 0.0, 100.0, 1.0 );
+ spinBox->setObjectName( "SliderValueSpinBox" );
spinBox->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed );
+ spinBox->setLayoutAlignmentHint( Qt::AlignCenter );
auto textInputBox = new TextInputBox();
textInputBox->setSizePolicy( Qt::Vertical, QskSizePolicy::Fixed );
auto textAreaBox = new TextAreaBox();
- auto vBox = new QskLinearBox( Qt::Vertical );
- vBox->setSpacing( 30 );
- vBox->setExtraSpacingAt( Qt::RightEdge | Qt::BottomEdge );
+ auto separator = new QskSeparator();
+ separator->setMargins( 5, 20, 5, 10 );
+ auto styleBox = new StyleComboBox();
+
+ auto vBox = new QskLinearBox( Qt::Vertical );
+ vBox->setSpacing( 20 );
vBox->addItem( sliders[0].continous );
vBox->addItem( sliders[0].discrete );
vBox->addItem( sliders[0].centered );
vBox->addItem( spinBox );
vBox->addItem( textInputBox );
- vBox->addItem( textAreaBox );
- auto mainBox = new QskLinearBox( Qt::Horizontal, this );
- mainBox->setSpacing( 30 );
- mainBox->addItem( sliders[1].continous );
- mainBox->addItem( sliders[1].discrete );
- mainBox->addItem( sliders[1].centered );
- mainBox->addItem( vBox );
+ auto hBox = new QskLinearBox( Qt::Horizontal );
+ hBox->setSpacing( 20 );
+ hBox->addItem( sliders[1].continous );
+ hBox->addItem( sliders[1].discrete );
+ hBox->addItem( sliders[1].centered );
+
+ auto gridBox = new QskGridBox( this );
+ gridBox->addItem( spinBox, 0, 0 );
+ gridBox->addItem( hBox, 1, 0, -1, 1 );
+ gridBox->addItem( vBox, 0, 1, 1, -1 );
+ gridBox->addItem( separator, 1, 1 );
+ gridBox->addItem( styleBox, 1, 2 );
+ gridBox->addItem( textInputBox, 2, 1, 1, -1 );
+ gridBox->addItem( textAreaBox, 3, 1, 1, -1 );
+ gridBox->setRowStretchFactor( 3, 10 );
+ gridBox->setColumnStretchFactor( 1, 10 );
auto inputs = findChildren< QskBoundedValueInput* >();
@@ -180,6 +237,11 @@ InputPage::InputPage( QQuickItem* parent )
}
spinBox->setValue( 30.0 );
+
+ connect( styleBox, &QskComboBox::currentIndexChanged,
+ textInputBox, &TextInputBox::setStyle );
+
+ styleBox->setCurrentIndex( QskTextField::OutlinedStyle );
}
void InputPage::syncValues( qreal value )
@@ -193,8 +255,7 @@ void InputPage::syncValues( qreal value )
if ( qobject_cast< const QskSlider* >( sender() ) )
{
- auto spinBoxes = findChildren< QskSpinBox* >();
- for ( auto spinBox : spinBoxes )
+ if ( auto spinBox = findChild< QskSpinBox* >( "SliderValueSpinBox" ) )
spinBox->setValue( value );
}
else
diff --git a/src/controls/QskTextField.cpp b/src/controls/QskTextField.cpp
index 904a3c6d..23355ab5 100644
--- a/src/controls/QskTextField.cpp
+++ b/src/controls/QskTextField.cpp
@@ -4,20 +4,43 @@
*****************************************************************************/
#include "QskTextField.h"
+#include "QskEvent.h"
+#include "QskFontRole.h"
+#include "QskQuick.h"
QSK_SUBCONTROL( QskTextField, Panel )
+
+QSK_SUBCONTROL( QskTextField, Header )
+QSK_SUBCONTROL( QskTextField, Footer )
+
+QSK_SUBCONTROL( QskTextField, Icon )
+QSK_SUBCONTROL( QskTextField, ButtonPanel )
+QSK_SUBCONTROL( QskTextField, Button )
QSK_SUBCONTROL( QskTextField, Placeholder )
+QSK_SUBCONTROL( QskTextField, CharacterCount )
+
+QSK_SYSTEM_STATE( QskTextField, Pressed, QskAspect::LastUserState << 1 )
+
class QskTextField::PrivateData
{
public:
+ QString headerText;
+ QString footerText;
QString placeholderText;
+
+ Style style = PlainStyle;
+ QskAspect::States buttonStates;
};
QskTextField::QskTextField( QQuickItem* parent )
: Inherited( parent )
, m_data( new PrivateData() )
{
+#if 1
+ // character count might have changed
+ connect( this, &QskTextInput::textChanged, this, &QQuickItem::update );
+#endif
}
QskTextField::QskTextField( const QString& text, QQuickItem* parent )
@@ -30,6 +53,74 @@ QskTextField::~QskTextField()
{
}
+void QskTextField::setStyle( Style style )
+{
+ if ( style != m_data->style )
+ {
+ m_data->style = style;
+
+ resetImplicitSize();
+ update();
+
+ Q_EMIT styleChanged( style );
+ }
+}
+
+QskTextField::Style QskTextField::style() const
+{
+ return m_data->style;
+}
+
+QString QskTextField::headerText() const
+{
+ return m_data->headerText;
+}
+
+void QskTextField::setHeaderText( const QString& text )
+{
+ if ( m_data->headerText != text )
+ {
+ m_data->headerText = text;
+
+ update();
+ resetImplicitSize();
+
+ Q_EMIT headerTextChanged( text );
+ }
+}
+
+QString QskTextField::footerText() const
+{
+ return m_data->footerText;
+}
+
+void QskTextField::setFooterText( const QString& text )
+{
+ if ( m_data->footerText != text )
+ {
+ m_data->footerText = text;
+
+ update();
+ resetImplicitSize();
+
+ Q_EMIT footerTextChanged( text );
+ }
+}
+
+QskGraphic QskTextField::icon() const
+{
+ return symbolHint( Icon );
+}
+
+void QskTextField::setIcon( const QskGraphic& icon )
+{
+ if ( setSymbolHint( Icon, icon ) )
+ {
+ update();
+ resetImplicitSize();
+ }
+}
+
void QskTextField::setPlaceholderText( const QString& text )
{
if ( m_data->placeholderText != text )
@@ -44,4 +135,106 @@ QString QskTextField::placeholderText() const
return m_data->placeholderText;
}
+QskAspect::Variation QskTextField::effectiveVariation() const
+{
+ return static_cast< QskAspect::Variation >( m_data->style );
+}
+
+void QskTextField::handleButtonClick()
+{
+ clear();
+ setEditing( true );
+}
+
+void QskTextField::mousePressEvent( QMouseEvent* event )
+{
+ if( !isReadOnly() )
+ {
+ const auto r = subControlRect( Button );
+ if ( r.contains( qskMousePosition( event ) ) )
+ {
+ setButtonState( Pressed, true );
+ return;
+ }
+ }
+
+ Inherited::mousePressEvent( event );
+}
+
+void QskTextField::mouseMoveEvent( QMouseEvent* event )
+{
+ if ( m_data->buttonStates & Pressed )
+ {
+ const auto r = subControlRect( Button );
+ setButtonState( Pressed, r.contains( qskMousePosition( event ) ) );
+ return;
+ }
+
+ Inherited::mouseMoveEvent( event );
+}
+
+void QskTextField::mouseReleaseEvent( QMouseEvent* event )
+{
+ if ( m_data->buttonStates & Pressed )
+ {
+ setButtonState( Pressed, false );
+ handleButtonClick();
+
+ return;
+ }
+
+ Inherited::mouseReleaseEvent( event );
+}
+
+void QskTextField::mouseUngrabEvent()
+{
+ setButtonState( Pressed, false );
+ Inherited::mouseUngrabEvent();
+}
+
+void QskTextField::hoverEnterEvent( QHoverEvent* event )
+{
+ Inherited::hoverEnterEvent( event );
+
+ const auto r = subControlRect( Button );
+ setButtonState( Hovered, r.contains( qskHoverPosition( event ) ) );
+}
+
+void QskTextField::hoverMoveEvent( QHoverEvent* event )
+{
+ const auto r = subControlRect( Button );
+ setButtonState( Hovered, r.contains( qskHoverPosition( event ) ) );
+
+ Inherited::hoverMoveEvent( event );
+}
+
+void QskTextField::hoverLeaveEvent( QHoverEvent* event )
+{
+ setButtonState( Hovered, false );
+ Inherited::hoverLeaveEvent( event );
+}
+
+QskAspect::States QskTextField::buttonStates() const
+{
+ auto states = skinStates() | m_data->buttonStates;
+
+ if ( !( m_data->buttonStates & Hovered ) )
+ states &= ~Hovered;
+
+ return states;
+}
+
+void QskTextField::setButtonState( QskAspect::State state, bool on )
+{
+ const auto oldStates = m_data->buttonStates;
+
+ if ( on )
+ m_data->buttonStates |= state;
+ else
+ m_data->buttonStates &= ~state;
+
+ if ( oldStates != m_data->buttonStates )
+ update();
+}
+
#include "moc_QskTextField.cpp"
diff --git a/src/controls/QskTextField.h b/src/controls/QskTextField.h
index e2e1f96a..139c61a8 100644
--- a/src/controls/QskTextField.h
+++ b/src/controls/QskTextField.h
@@ -7,31 +7,88 @@
#define QSK_TEXT_FIELD_H
#include "QskTextInput.h"
+#include "QskGraphic.h"
class QSK_EXPORT QskTextField : public QskTextInput
{
Q_OBJECT
+ Q_PROPERTY( QString headerText READ headerText
+ WRITE setHeaderText NOTIFY headerTextChanged )
+
+ Q_PROPERTY( QString footerText READ footerText
+ WRITE setFooterText NOTIFY footerTextChanged )
+
Q_PROPERTY( QString placeholderText READ placeholderText
WRITE setPlaceholderText NOTIFY placeholderTextChanged )
+ Q_PROPERTY( Style style READ style
+ WRITE setStyle NOTIFY styleChanged )
+
using Inherited = QskTextInput;
public:
- QSK_SUBCONTROLS( Panel, Placeholder )
+ QSK_STATES( Pressed )
+
+ QSK_SUBCONTROLS( Panel, Header, Footer, Placeholder,
+ Icon, Button, ButtonPanel, CharacterCount )
+
+ enum Style : quint8
+ {
+ PlainStyle,
+
+ OutlinedStyle,
+ FilledStyle
+ };
+ Q_ENUM( Style )
QskTextField( QQuickItem* parent = nullptr );
QskTextField( const QString& text, QQuickItem* parent = nullptr );
~QskTextField() override;
+ void setStyle( Style );
+ Style style() const;
+
+ void setHeaderText( const QString& );
+ QString headerText() const;
+
+ void setFooterText( const QString& );
+ QString footerText() const;
+
+ QskGraphic icon() const;
+ void setIcon( const QskGraphic& );
+
void setPlaceholderText( const QString& );
QString placeholderText() const;
+ QskAspect::Variation effectiveVariation() const override;
+
+#if 1
+ QskAspect::States buttonStates() const;
+#endif
+
Q_SIGNALS:
+ void headerTextChanged( const QString& );
+ void footerTextChanged( const QString& );
void placeholderTextChanged( const QString& );
+ void styleChanged( Style );
+
+ protected:
+ void hoverEnterEvent( QHoverEvent* ) override;
+ void hoverMoveEvent( QHoverEvent* ) override;
+ void hoverLeaveEvent( QHoverEvent* ) override;
+
+ void mousePressEvent( QMouseEvent* ) override;
+ void mouseMoveEvent( QMouseEvent* ) override;
+ void mouseReleaseEvent( QMouseEvent* ) override;
+ void mouseUngrabEvent() override;
+
+ virtual void handleButtonClick();
private:
+ void setButtonState( QskAspect::State, bool );
+
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};
diff --git a/src/controls/QskTextFieldSkinlet.cpp b/src/controls/QskTextFieldSkinlet.cpp
index b070e580..7b1e4e70 100644
--- a/src/controls/QskTextFieldSkinlet.cpp
+++ b/src/controls/QskTextFieldSkinlet.cpp
@@ -6,12 +6,16 @@
#include "QskTextFieldSkinlet.h"
#include "QskTextField.h"
+#include
+
using Q = QskTextField;
QskTextFieldSkinlet::QskTextFieldSkinlet( QskSkin* skin )
: Inherited( skin )
{
- setNodeRoles( { PanelRole, TextPanelRole, PlaceholderRole } );
+ setNodeRoles( { PanelRole, TextPanelRole,
+ IconRole, ButtonPanelRole, ButtonRole,
+ PlaceholderRole, HeaderRole, FooterRole } );
}
QskTextFieldSkinlet::~QskTextFieldSkinlet()
@@ -28,7 +32,30 @@ QRectF QskTextFieldSkinlet::subControlRect( const QskSkinnable* skinnable,
return skinnable->subControlContentsRect( contentsRect, Q::Panel );
if ( subControl == Q::Text )
- return skinnable->subControlContentsRect( contentsRect, Q::TextPanel );
+ {
+ auto rect = skinnable->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() );
+ }
+
+ if( !skinnable->symbolHint( Q::Button ).isEmpty() )
+ {
+ const auto r = subControlRect( skinnable, contentsRect, Q::Button );
+ if( !r.isEmpty() )
+ rect.setRight( r.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 )
{
@@ -39,6 +66,60 @@ QRectF QskTextFieldSkinlet::subControlRect( const QskSkinnable* skinnable,
return QRectF();
}
+ if ( subControl == Q::Icon )
+ {
+ if( !skinnable->symbolHint( subControl ).isEmpty() )
+ {
+ const auto panelRect = skinnable->subControlContentsRect(
+ contentsRect, Q::TextPanel );
+
+ auto rect = panelRect;
+
+ rect.setSize( skinnable->strutSizeHint( subControl ) );
+ rect.moveCenter( { rect.center().x(), panelRect.center().y() } );
+
+ return rect;
+ }
+
+ return QRectF();
+ }
+
+ 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 );
+
+ QRectF rect( QPointF(), skinnable->strutSizeHint( subControl ) );
+ rect.moveCenter( r.center() );
+
+ return rect;
+ }
+
+ return QRectF();
+ }
+
+ if ( subControl == Q::Button )
+ {
+ if( !skinnable->symbolHint( subControl ).isEmpty() )
+ {
+ const auto panelRect = skinnable->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 QRectF();
+ }
+
return Inherited::subControlRect( skinnable, contentsRect, subControl );
}
@@ -76,6 +157,27 @@ QSGNode* QskTextFieldSkinlet::updateSubNode(
textField->alignmentHint( subControl, Qt::AlignLeft ),
options, text, subControl );
}
+
+ case HeaderRole:
+ {
+ return updateTextNode( skinnable, node,
+ textField->headerText(), Q::Header );
+ }
+
+ case FooterRole:
+ {
+ return updateTextNode( skinnable, node,
+ textField->footerText(), Q::Footer );
+ }
+
+ case IconRole:
+ return updateSymbolNode( skinnable, node, Q::Icon );
+
+ case ButtonPanelRole:
+ return updateBoxNode( skinnable, node, Q::ButtonPanel );
+
+ case ButtonRole:
+ return updateSymbolNode( skinnable, node, Q::Button );
}
return Inherited::updateSubNode( skinnable, nodeRole, node );
diff --git a/src/controls/QskTextFieldSkinlet.h b/src/controls/QskTextFieldSkinlet.h
index d5cb3b7d..14380363 100644
--- a/src/controls/QskTextFieldSkinlet.h
+++ b/src/controls/QskTextFieldSkinlet.h
@@ -21,7 +21,14 @@ class QSK_EXPORT QskTextFieldSkinlet : public QskSkinlet
{
PanelRole,
TextPanelRole,
+
+ HeaderRole,
+ FooterRole,
PlaceholderRole,
+ IconRole,
+ ButtonPanelRole,
+ ButtonRole,
+
RoleCount
};