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 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 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
};