Compare commits

..

11 Commits

Author SHA1 Message Date
Uwe Rathmann c7012fe24a material specific decorations generalized and moved to QskTextFieldSkinlet 2025-05-15 14:00:07 +02:00
Uwe Rathmann 7305502de0 from master 2025-05-15 11:46:15 +02:00
Uwe Rathmann cc5229f7a3 from master 2025-05-15 11:41:27 +02:00
Uwe Rathmann 23a7859de6 minor code rearrangements 2025-03-17 11:37:06 +01:00
Uwe Rathmann 36bea57477 Outlined/Filled moved to gallery code 2025-03-14 14:06:38 +01:00
Uwe Rathmann 2c34318c00 Merge branch 'master' into features/textinputs 2025-03-14 10:48:27 +01:00
Uwe Rathmann 38e1820272 Merge branch 'master' into features/textinputs 2025-03-12 13:37:25 +01:00
Uwe Rathmann 07e425195b Merge branch 'master' into features/textinputs 2025-03-10 15:02:08 +01:00
Uwe Rathmann edad453505 QskTextField wip 2025-03-10 15:01:29 +01:00
Uwe Rathmann 04fbb2a2a1 Merge branch 'master' into features/textinputs 2025-02-28 09:41:29 +01:00
Uwe Rathmann 5da7aa8211 rebased from master 2025-02-07 11:35:36 +01:00
28 changed files with 1039 additions and 213 deletions

View File

@ -7,15 +7,10 @@ list(APPEND HEADERS
QskFluent2Global.h QskFluent2Theme.h QskFluent2SkinFactory.h QskFluent2Global.h QskFluent2Theme.h QskFluent2SkinFactory.h
) )
list(APPEND PRIVATE_HEADERS
QskFluent2TextFieldSkinlet.h
)
list(APPEND SOURCES list(APPEND SOURCES
QskFluent2Theme.cpp QskFluent2Theme.cpp
QskFluent2Skin.cpp QskFluent2Skin.cpp
QskFluent2SkinFactory.cpp QskFluent2SkinFactory.cpp
QskFluent2TextFieldSkinlet.cpp
) )
qt_add_resources(SOURCES QskFluent2Icons.qrc) qt_add_resources(SOURCES QskFluent2Icons.qrc)

View File

@ -3,5 +3,7 @@
<file>icons/qvg/checkmark.qvg</file> <file>icons/qvg/checkmark.qvg</file>
<file>icons/qvg/chevron_down.qvg</file> <file>icons/qvg/chevron_down.qvg</file>
<file>icons/qvg/chevron_up.qvg</file> <file>icons/qvg/chevron_up.qvg</file>
<file>icons/qvg/dismiss.qvg</file>
<file>icons/qvg/search.qvg</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -47,7 +47,6 @@
*/ */
#include "QskFluent2Skin.h" #include "QskFluent2Skin.h"
#include "QskFluent2Theme.h" #include "QskFluent2Theme.h"
#include "QskFluent2TextFieldSkinlet.h"
#include <QskTextAreaSkinlet.h> #include <QskTextAreaSkinlet.h>
#include <QskSkinHintTableEditor.h> #include <QskSkinHintTableEditor.h>
@ -1854,11 +1853,13 @@ void Editor::setupTextAreaColors(
setBoxBorderGradient( panel, borderColor1, borderColor2, panelColor ); setBoxBorderGradient( panel, borderColor1, borderColor2, panelColor );
} }
} }
void Editor::setupTextFieldMetrics() void Editor::setupTextFieldMetrics()
{ {
using Q = QskTextField; using Q = QskTextField;
setFontRole( Q::Text, Fluent2::Body ); setFontRole( Q::Header, Fluent2::Body );
setFontRole( Q::Footer, Fluent2::Caption );
setStrutSize( Q::TextPanel, { -1, 30_px } ); setStrutSize( Q::TextPanel, { -1, 30_px } );
setPadding( Q::TextPanel, { 11_px, 0, 11_px, 0 } ); setPadding( Q::TextPanel, { 11_px, 0, 11_px, 0 } );
@ -1869,8 +1870,23 @@ void Editor::setupTextFieldMetrics()
setBoxShape( Q::TextPanel, 3_px ); setBoxShape( Q::TextPanel, 3_px );
setAlignment( Q::Placeholder, Qt::AlignLeft | Qt::AlignVCenter ); for ( const auto subControl : { Q::Text, Q::Placeholder } )
setFontRole( Q::Placeholder, fontRole( Q::Text ) ); {
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" ) );
for ( const auto subControl : { Q::Icon, Q::Button } )
{
setMargin( subControl, 2_px );
setStrutSize( subControl, 16_px, 16_px );
}
} }
void Editor::setupTextFieldColors( void Editor::setupTextFieldColors(
@ -1878,54 +1894,66 @@ void Editor::setupTextFieldColors(
{ {
using Q = QskTextField; using Q = QskTextField;
using A = QskAspect; using A = QskAspect;
using W = QskFluent2Skin;
const auto& pal = theme.palette; const auto& pal = theme.palette;
const auto text = Q::Text | section; setColor( Q::Text | section | Q::Selected, pal.fillColor.textOnAccent.selectedText );
setColor( Q::TextPanel | section | Q::Selected, pal.fillColor.accent.selectedTextBackground );
#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::TextPanel | Q::Selected, pal.fillColor.accent.selectedTextBackground ); setColor( Q::Placeholder | section, pal.fillColor.text.secondary );
setColor( Q::Placeholder, 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 } ) 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 ) if ( state == Q::Hovered )
{ {
panelColor = pal.fillColor.control.secondary; panelColor = pal.fillColor.control.secondary;
borderColor1 = pal.elevation.textControl.border[0]; borderColor1 = pal.elevation.textControl.border[0];
borderColor2 = pal.elevation.textControl.border[1]; borderColor2 = pal.elevation.textControl.border[1];
textColor = pal.fillColor.text.primary;
} }
else if ( ( state == Q::Focused ) || ( state == Q::Editing ) ) else if ( ( state == Q::Focused ) || ( state == Q::Editing ) )
{ {
panelColor = pal.fillColor.control.inputActive; panelColor = pal.fillColor.control.inputActive;
borderColor1 = pal.elevation.textControl.border[0]; borderColor1 = pal.elevation.textControl.border[0];
borderColor2 = pal.fillColor.accent.defaultColor; borderColor2 = pal.fillColor.accent.defaultColor;
textColor = pal.fillColor.text.primary;
} }
else if ( state == Q::Disabled ) else if ( state == Q::Disabled )
{ {
panelColor = pal.fillColor.control.disabled; panelColor = pal.fillColor.control.disabled;
borderColor1 = borderColor2 = pal.strokeColor.control.defaultColor; borderColor1 = borderColor2 = pal.strokeColor.control.defaultColor;
textColor = pal.fillColor.text.disabled;
} }
else // A::NoState else // A::NoState
{ {
panelColor = pal.fillColor.control.defaultColor; panelColor = pal.fillColor.control.defaultColor;
borderColor1 = pal.elevation.textControl.border[0]; borderColor1 = pal.elevation.textControl.border[0];
borderColor2 = pal.elevation.textControl.border[1]; borderColor2 = pal.elevation.textControl.border[1];
textColor = pal.fillColor.text.primary;
} }
const auto panel = Q::TextPanel | section | state; const auto panel = Q::TextPanel | section | state;
const auto text = Q::Text | section | state;
panelColor = rgbSolid( panelColor, pal.background.solid.base ); panelColor = rgbSolid( panelColor, pal.background.solid.base );
setGradient( panel, panelColor ); setGradient( panel, panelColor );
setBoxBorderGradient( panel, borderColor1, borderColor2, 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 );
} }
} }
@ -2113,8 +2141,6 @@ void Editor::setupVirtualKeyboardColors(
QskFluent2Skin::QskFluent2Skin( QObject* parent ) QskFluent2Skin::QskFluent2Skin( QObject* parent )
: Inherited( parent ) : Inherited( parent )
{ {
declareSkinlet< QskTextField, QskFluent2TextFieldSkinlet >();
setupFonts(); setupFonts();
Editor editor( &hintTable() ); Editor editor( &hintTable() );

View File

@ -1,32 +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
{
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 );
}
#include "moc_QskFluent2TextFieldSkinlet.cpp"

View File

@ -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

View File

@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.08859 2.21569L2.14645 2.14645C2.32001 1.97288 2.58944 1.9536 2.78431 2.08859L2.85355 2.14645L6 5.293L9.14645 2.14645C9.34171 1.95118 9.65829 1.95118 9.85355 2.14645C10.0488 2.34171 10.0488 2.65829 9.85355 2.85355L6.707 6L9.85355 9.14645C10.0271 9.32001 10.0464 9.58944 9.91141 9.78431L9.85355 9.85355C9.67999 10.0271 9.41056 10.0464 9.21569 9.91141L9.14645 9.85355L6 6.707L2.85355 9.85355C2.65829 10.0488 2.34171 10.0488 2.14645 9.85355C1.95118 9.65829 1.95118 9.34171 2.14645 9.14645L5.293 6L2.14645 2.85355C1.97288 2.67999 1.9536 2.41056 2.08859 2.21569L2.14645 2.14645L2.08859 2.21569Z" fill="#212121"/>
</svg>

After

Width:  |  Height:  |  Size: 722 B

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 1C2.79086 1 1 2.79086 1 5C1 7.20914 2.79086 9 5 9C5.92417 9 6.77513 8.68659 7.45241 8.16025L10.1455 10.8533C10.3408 11.0486 10.6574 11.0486 10.8526 10.8533C11.0479 10.6581 11.0479 10.3415 10.8526 10.1462L8.15961 7.45323C8.68633 6.77583 9 5.92454 9 5C9 2.79086 7.20914 1 5 1ZM2 5C2 3.34315 3.34315 2 5 2C6.65685 2 8 3.34315 8 5C8 6.65685 6.65685 8 5 8C3.34315 8 2 6.65685 2 5Z" fill="#212121"/>
</svg>

After

Width:  |  Height:  |  Size: 509 B

View File

@ -425,6 +425,9 @@ void Editor::setupTextField()
setBoxShape( Q::TextPanel, 2_px ); setBoxShape( Q::TextPanel, 2_px );
setPadding( Q::TextPanel, 4_px ); setPadding( Q::TextPanel, 4_px );
setFontRole( Q::Header, QskFontRole::Body );
setFontRole( Q::Footer, QskFontRole::Caption );
} }
void Editor::setupTextArea() void Editor::setupTextArea()

View File

@ -6,5 +6,8 @@
<file>icons/qvg/check.qvg</file> <file>icons/qvg/check.qvg</file>
<file>icons/qvg/close.qvg</file> <file>icons/qvg/close.qvg</file>
<file>icons/qvg/remove.qvg</file> <file>icons/qvg/remove.qvg</file>
<file>icons/qvg/text_field_search.qvg</file>
<file>icons/qvg/text_field_cancel.qvg</file>
<file>icons/qvg/text_field_error.qvg</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -476,43 +476,143 @@ void Editor::setupTextArea()
void Editor::setupTextField() void Editor::setupTextField()
{ {
using Q = QskTextField; using Q = QskTextField;
using M3 = QskMaterial3Skin;
using A = QskAspect;
setColor( Q::Text, m_pal.onSurface ); setColor( Q::Text, m_pal.onSurface );
setFontRole( Q::Text, BodyLarge ); setFontRole( Q::Text, BodyLarge );
setColor( Q::Text | Q::Disabled, m_pal.onSurface38 ); setColor( Q::Text | Q::Disabled, m_pal.onSurface38 );
setStrutSize( Q::Panel, -1.0, 56_px ); const auto Outlined = A::NoVariation;
setPadding( Q::Panel, { 12_px, 8_px, 12_px, 8_px } ); const auto Filled = static_cast< A::Variation >( 1 );
setGradient( Q::Panel, m_pal.surfaceVariant );
const auto activeStates = Q::Focused | Q::Editing;
{
// TextPanel
setAnimation( Q::TextPanel | A::Color, qskDuration );
setAnimation( Q::TextPanel | A::Metric, qskDuration );
}
{
const auto aspect = Q::TextPanel | Filled;
setBoxBorderColors( aspect, m_pal.onSurfaceVariant );
setGradient( aspect, m_pal.surfaceVariant );
setGradient( aspect | Q::Hovered,
m_pal.hoverColor( m_pal.onSurfaceVariant, m_pal.surfaceVariant ),
{ QskStateCombination::CombinationNoState, activeStates | Q::Error } );
setGradient( aspect | Q::Disabled,
QskRgb::toTransparentF( m_pal.onSurface, 0.04 ) );
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 ); 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 hoverColor = flattenedColor( m_pal.onSurfaceVariant, // Icon
m_pal.surfaceVariant, m_pal.hoverOpacity );
setGradient( Q::Panel | Q::Hovered, hoverColor );
const auto focusColor = flattenedColor( m_pal.onSurfaceVariant, setStrutSize( Q::Icon, { 24_px, 24_px } );
m_pal.surfaceVariant, m_pal.focusOpacity ); setMargin( Q::Icon, 2_px );
setGradient( Q::Panel | Q::Focused, focusColor ); setSymbol( Q::Icon, symbol( "text_field_search" ) );
// ### Also add a pressed state setGraphicRole( Q::Icon, M3::GraphicRoleOnSurface );
setGraphicRole( Q::Icon | Q::Error, M3::GraphicRoleOnSurfaceVariant );
setFontRole( Q::Text, BodyLarge ); setGraphicRole( Q::Icon | Q::Disabled, M3::GraphicRoleOnSurface38 );
setAlignment( Q::Placeholder, Qt::AlignLeft | Qt::AlignVCenter ); // Button
const auto disabledPanelColor = QskRgb::toTransparentF( m_pal.onSurface, 0.04 ); setStrutSize( Q::Button, { 24_px, 24_px } );
setGradient( Q::Panel | Q::Disabled, disabledPanelColor ); setMargin( Q::Button, 2_px );
setBoxBorderColors( Q::Panel | Q::Disabled, m_pal.onSurface38 ); setGraphicRole( Q::Button, M3::GraphicRoleOnSurfaceVariant );
setSymbol( Q::Button, symbol( "text_field_cancel" ) );
// PlaceholderText 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 );
setColor( Q::Placeholder, color( Q::Text ) ); setGraphicRole( Q::Button | Q::Disabled, M3::GraphicRoleOnSurface38 );
setFontRole( Q::Placeholder, BodyLarge );
setAlignment( Q::Placeholder, Qt::AlignLeft | Qt::AlignVCenter ); // ButtonPanel
setStrutSize( Q::ButtonPanel, { 45_px, 45_px } );
setGradient( Q::ButtonPanel | Q::Hovered, m_pal.onSurface8 );
setBoxShape( Q::ButtonPanel, 100, Qt::RelativeSize );
// Header
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 );
}
setAlignment( Q::Footer, Qt::AlignLeft | Qt::AlignVCenter );
setAlignment( Q::CharacterCount, Qt::AlignRight | Qt::AlignVCenter );
setFlag( Q::CharacterCount | A::Option, Qsk::Maybe );
} }
void Editor::setupProgressBar() void Editor::setupProgressBar()
@ -1617,6 +1717,7 @@ QskMaterial3Theme::QskMaterial3Theme( QskSkin::ColorScheme colorScheme,
elevation2 = QskShadowMetrics( -2, 8, { 0, 2 } ); elevation2 = QskShadowMetrics( -2, 8, { 0, 2 } );
elevation3 = QskShadowMetrics( -1, 11, { 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 ); shapeExtraSmallTop = QskBoxShapeMetrics( 4_px, 4_px, 0, 0 );
} }
@ -1743,6 +1844,7 @@ void QskMaterial3Skin::setupGraphicFilters( const QskMaterial3Theme& theme )
setGraphicColor( GraphicRoleOnPrimaryContainer, theme.onPrimaryContainer ); setGraphicColor( GraphicRoleOnPrimaryContainer, theme.onPrimaryContainer );
setGraphicColor( GraphicRoleOnSecondaryContainer, theme.onSecondaryContainer ); setGraphicColor( GraphicRoleOnSecondaryContainer, theme.onSecondaryContainer );
setGraphicColor( GraphicRoleOnError, theme.onError ); setGraphicColor( GraphicRoleOnError, theme.onError );
setGraphicColor( GraphicRoleOnErrorContainer, theme.onErrorContainer );
setGraphicColor( GraphicRoleOnSurface, theme.onSurface ); setGraphicColor( GraphicRoleOnSurface, theme.onSurface );
setGraphicColor( GraphicRoleOnSurface38, theme.onSurface38 ); setGraphicColor( GraphicRoleOnSurface38, theme.onSurface38 );
setGraphicColor( GraphicRoleOnSurfaceVariant, theme.onSurfaceVariant ); setGraphicColor( GraphicRoleOnSurfaceVariant, theme.onSurfaceVariant );

View File

@ -107,6 +107,7 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Theme
qreal stateOpacity( int state ) const; qreal stateOpacity( int state ) const;
QskBoxShapeMetrics shapeExtraSmall;
QskBoxShapeMetrics shapeExtraSmallTop; QskBoxShapeMetrics shapeExtraSmallTop;
}; };
@ -119,7 +120,9 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Skin : public QskSkin
public: public:
enum GraphicRole enum GraphicRole
{ {
GraphicRoleError,
GraphicRoleOnError, GraphicRoleOnError,
GraphicRoleOnErrorContainer,
GraphicRoleOnPrimary, GraphicRoleOnPrimary,
GraphicRoleOnPrimaryContainer, GraphicRoleOnPrimaryContainer,
GraphicRoleOnSecondaryContainer, GraphicRoleOnSecondaryContainer,

View File

@ -4,33 +4,28 @@
*****************************************************************************/ *****************************************************************************/
#include "QskMaterial3TextFieldSkinlet.h" #include "QskMaterial3TextFieldSkinlet.h"
#include "QskTextField.h" #include "QskMaterial3Skin.h"
#include <QskSkinnable.h>
static inline bool isOutlined( const QskSkinnable* skinnable )
{
return skinnable->effectiveVariation() == QskAspect::NoVariation;
}
QskMaterial3TextFieldSkinlet::QskMaterial3TextFieldSkinlet( QskSkin* skin ) QskMaterial3TextFieldSkinlet::QskMaterial3TextFieldSkinlet( QskSkin* skin )
: Inherited( skin ) : Inherited( skin )
{ {
setRenderHints( UseHeaderAsPlaceholder );
} }
QskMaterial3TextFieldSkinlet::~QskMaterial3TextFieldSkinlet() QskMaterial3TextFieldSkinlet::~QskMaterial3TextFieldSkinlet()
{ {
} }
QRectF QskMaterial3TextFieldSkinlet::subControlRect( const QskSkinnable* skinnable, int QskMaterial3TextFieldSkinlet::panelMode( const QskSkinnable* skinnable ) const
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
{ {
return Inherited::subControlRect( skinnable, contentsRect, subControl ); return isOutlined( skinnable ) ? 2 : 1;
}
QSGNode* QskMaterial3TextFieldSkinlet::updateSubNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
{
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 );
} }
#include "moc_QskMaterial3TextFieldSkinlet.cpp" #include "moc_QskMaterial3TextFieldSkinlet.cpp"

View File

@ -19,15 +19,8 @@ class QSK_MATERIAL3_EXPORT QskMaterial3TextFieldSkinlet : public QskTextFieldSki
Q_INVOKABLE QskMaterial3TextFieldSkinlet( QskSkin* = nullptr ); Q_INVOKABLE QskMaterial3TextFieldSkinlet( QskSkin* = nullptr );
~QskMaterial3TextFieldSkinlet() override; ~QskMaterial3TextFieldSkinlet() override;
QRectF subControlRect( const QskSkinnable*, private:
const QRectF& rect, QskAspect::Subcontrol ) const override; int panelMode( const QskSkinnable* skinnable ) const override final;
QSizeF sizeHint( const QskSkinnable*,
Qt::SizeHint, const QSizeF& ) const override;
protected:
QSGNode* updateSubNode( const QskSkinnable*,
quint8 nodeRole, QSGNode* ) const override;
}; };
#endif #endif

Binary file not shown.

View File

@ -0,0 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 0C4.47 0 0 4.47 0 10C0 15.53 4.47 20 10 20C15.53 20 20 15.53 20 10C20 4.47 15.53 0 10 0ZM10 18C5.59 18 2 14.41 2 10C2 5.59 5.59 2 10 2C14.41 2 18 5.59 18 10C18 14.41 14.41 18 10 18ZM10 8.59L13.59 5L15 6.41L11.41 10L15 13.59L13.59 15L10 11.41L6.41 15L5 13.59L8.59 10L5 6.41L6.41 5L10 8.59Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 462 B

View File

@ -0,0 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 0C4.48 0 0 4.48 0 10C0 15.52 4.48 20 10 20C15.52 20 20 15.52 20 10C20 4.48 15.52 0 10 0ZM9 15V13H11V15H9ZM9 5V11H11V5H9Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 294 B

View File

@ -0,0 +1,4 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.76 10.27L17.49 16L16 17.49L10.27 11.76C9.2 12.53 7.91 13 6.5 13C2.91 13 0 10.09 0 6.5C0 2.91 2.91 0 6.5 0C10.09 0 13 2.91 13 6.5C13 7.91 12.53 9.2 11.76 10.27ZM6.5 2C4.01 2 2 4.01 2 6.5C2 8.99 4.01 11 6.5 11C8.99 11 11 8.99 11 6.5C11 4.01 8.99 2 6.5 2Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 426 B

View File

@ -10,6 +10,12 @@
#include <QskTextArea.h> #include <QskTextArea.h>
#include <QskTextField.h> #include <QskTextField.h>
#include <QskSpinBox.h> #include <QskSpinBox.h>
#include <QskComboBox.h>
#include <QskSeparator.h>
#include <QskFunctions.h>
#include <QskFontRole.h>
#include <QFontMetricsF>
namespace namespace
{ {
@ -62,40 +68,108 @@ namespace
} }
}; };
class TextField : public QskTextField
{
public:
enum Style
{
OutlinedStyle,
FilledStyle
};
TextField( QQuickItem* parent = nullptr )
: QskTextField( parent )
{
}
void setStyle( Style style )
{
if ( style != m_style )
{
m_style = style;
resetImplicitSize();
polish();
update();
}
}
Style style() const
{
return m_style;
}
QskAspect::Variation effectiveVariation() const override
{
return static_cast< QskAspect::Variation >( m_style );
}
private:
Style m_style = OutlinedStyle;
};
class TextInputBox : public QskLinearBox class TextInputBox : public QskLinearBox
{ {
public: public:
TextInputBox( QQuickItem* parent = nullptr ) TextInputBox( QQuickItem* parent = nullptr )
: QskLinearBox( Qt::Horizontal, parent ) : QskLinearBox( Qt::Horizontal, 3, parent )
{ {
setSpacing( 20 ); setSpacing( 20 );
setDefaultAlignment( Qt::AlignHCenter | Qt::AlignTop );
{ {
{ auto field = new TextField( this );
auto field = new QskTextField( this ); field->setHeaderText( "Name" );
field->setText( "John Doe" ); field->setText( "John Doe" );
field->setPlaceholderText( "<Name>" ); field->setPlaceholderText( "<Name>" );
field->setFooterText( "Required *" );
}
#if 0 {
connect( field, &QskTextField::textChanged, auto field = new TextField( this );
[field]() { qDebug() << "Text:" << field->text(); } ); field->setHeaderText( "Nickname" );
#endif field->setPlaceholderText( "<Nickname>" );
field->setFooterText( "Optional" );
}
{
auto field = new TextField( this );
field->setIcon( {} );
field->setPlaceholderText( "<no header>" );
}
} {
auto field = new TextField( this );
field->setSkinStateFlag( QskTextField::Error );
field->setText( "Error Text" );
field->setHeaderText( "error" );
field->setPlaceholderText( "<text>" );
field->setFooterText( "error text" );
}
{ {
auto field = new QskTextField( this ); auto field = new TextField( this );
field->setReadOnly( true ); field->setReadOnly( true );
field->setText( "Read Only" ); field->setText( "Read Only" );
field->setSizePolicy( Qt::Horizontal, QskSizePolicy::MinimumExpanding ); field->setHeaderText( "read only" );
} field->setSizePolicy( Qt::Horizontal, QskSizePolicy::MinimumExpanding );
}
{ {
auto field = new QskTextField( this ); auto field = new TextField( this );
field->setMaxLength( 5 ); field->setMaxLength( 15 );
field->setEchoMode( QskTextField::Password ); field->setHeaderText( "password" );
field->setPlaceholderText( "<password>" ); field->setEchoMode( QskTextField::Password );
} field->setPlaceholderText( "<password>" );
}
}
void setStyle( int value )
{
auto textFields = findChildren< QskTextField* >();
for ( auto field : textFields )
{
const auto style = static_cast< TextField::Style >( value );
dynamic_cast< TextField* >(field)->setStyle( style );
} }
} }
}; };
@ -120,6 +194,17 @@ namespace
} }
} }
}; };
class StyleComboBox : public QskComboBox
{
public:
StyleComboBox( QQuickItem* parent = nullptr )
: QskComboBox( parent )
{
addOption( QString(), "Outlined" );
addOption( QString(), "Filled" );
}
};
} }
InputPage::InputPage( QQuickItem* parent ) InputPage::InputPage( QQuickItem* parent )
@ -146,30 +231,44 @@ InputPage::InputPage( QQuickItem* parent )
} }
auto spinBox = new QskSpinBox( 0.0, 100.0, 1.0 ); auto spinBox = new QskSpinBox( 0.0, 100.0, 1.0 );
spinBox->setObjectName( "SliderValueSpinBox" );
spinBox->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed ); spinBox->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed );
spinBox->setLayoutAlignmentHint( Qt::AlignCenter );
auto textInputBox = new TextInputBox(); auto textInputBox = new TextInputBox();
textInputBox->setSizePolicy( Qt::Vertical, QskSizePolicy::Fixed ); textInputBox->setSizePolicy( Qt::Vertical, QskSizePolicy::Fixed );
auto textAreaBox = new TextAreaBox(); auto textAreaBox = new TextAreaBox();
auto vBox = new QskLinearBox( Qt::Vertical ); auto separator = new QskSeparator();
vBox->setSpacing( 30 ); separator->setMargins( 5, 20, 5, 10 );
vBox->setExtraSpacingAt( Qt::RightEdge | Qt::BottomEdge );
auto styleBox = new StyleComboBox();
auto vBox = new QskLinearBox( Qt::Vertical );
vBox->setSpacing( 20 );
vBox->addItem( sliders[0].continous ); vBox->addItem( sliders[0].continous );
vBox->addItem( sliders[0].discrete ); vBox->addItem( sliders[0].discrete );
vBox->addItem( sliders[0].centered ); vBox->addItem( sliders[0].centered );
vBox->addItem( spinBox ); vBox->addItem( spinBox );
vBox->addItem( textInputBox ); vBox->addItem( textInputBox );
vBox->addItem( textAreaBox );
auto mainBox = new QskLinearBox( Qt::Horizontal, this ); auto hBox = new QskLinearBox( Qt::Horizontal );
mainBox->setSpacing( 30 ); hBox->setSpacing( 20 );
mainBox->addItem( sliders[1].continous ); hBox->addItem( sliders[1].continous );
mainBox->addItem( sliders[1].discrete ); hBox->addItem( sliders[1].discrete );
mainBox->addItem( sliders[1].centered ); hBox->addItem( sliders[1].centered );
mainBox->addItem( vBox );
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* >(); auto inputs = findChildren< QskBoundedValueInput* >();
@ -180,6 +279,11 @@ InputPage::InputPage( QQuickItem* parent )
} }
spinBox->setValue( 30.0 ); spinBox->setValue( 30.0 );
connect( styleBox, &QskComboBox::currentIndexChanged,
textInputBox, &TextInputBox::setStyle );
styleBox->setCurrentIndex( TextField::OutlinedStyle );
} }
void InputPage::syncValues( qreal value ) void InputPage::syncValues( qreal value )
@ -193,8 +297,7 @@ void InputPage::syncValues( qreal value )
if ( qobject_cast< const QskSlider* >( sender() ) ) if ( qobject_cast< const QskSlider* >( sender() ) )
{ {
auto spinBoxes = findChildren< QskSpinBox* >(); if ( auto spinBox = findChild< QskSpinBox* >( "SliderValueSpinBox" ) )
for ( auto spinBox : spinBoxes )
spinBox->setValue( value ); spinBox->setValue( value );
} }
else else

View File

@ -51,12 +51,14 @@ namespace
new QskTextLabel( label, this ); new QskTextLabel( label, this );
m_textField = new QskTextField( 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->setValidator( new InputValidator( m_textField ) );
m_textField->setText( QString::number( value ) ); m_textField->setText( QString::number( value ) );
const QFontMetricsF fm( m_textField->font() );
m_textField->setFixedWidth( fm.horizontalAdvance( "-0.000" ) );
connect( m_textField, &QskTextField::editingChanged, connect( m_textField, &QskTextField::editingChanged,
this, [ this ]( bool on ) { if ( !on ) Q_EMIT valueChanged(); } ); this, [ this ]( bool on ) { if ( !on ) Q_EMIT valueChanged(); } );

View File

@ -4,20 +4,42 @@
*****************************************************************************/ *****************************************************************************/
#include "QskTextField.h" #include "QskTextField.h"
#include "QskEvent.h"
#include "QskFontRole.h"
#include "QskQuick.h"
QSK_SUBCONTROL( QskTextField, Panel ) 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, Placeholder )
QSK_SUBCONTROL( QskTextField, CharacterCount )
QSK_SYSTEM_STATE( QskTextField, Pressed, QskAspect::LastUserState << 1 )
class QskTextField::PrivateData class QskTextField::PrivateData
{ {
public: public:
QString headerText;
QString footerText;
QString placeholderText; QString placeholderText;
QskAspect::States buttonStates;
}; };
QskTextField::QskTextField( QQuickItem* parent ) QskTextField::QskTextField( QQuickItem* parent )
: Inherited( parent ) : Inherited( parent )
, m_data( new PrivateData() ) , 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 ) QskTextField::QskTextField( const QString& text, QQuickItem* parent )
@ -30,6 +52,56 @@ QskTextField::~QskTextField()
{ {
} }
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 ) void QskTextField::setPlaceholderText( const QString& text )
{ {
if ( m_data->placeholderText != text ) if ( m_data->placeholderText != text )
@ -44,4 +116,101 @@ QString QskTextField::placeholderText() const
return m_data->placeholderText; return m_data->placeholderText;
} }
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" #include "moc_QskTextField.cpp"

View File

@ -7,31 +7,70 @@
#define QSK_TEXT_FIELD_H #define QSK_TEXT_FIELD_H
#include "QskTextInput.h" #include "QskTextInput.h"
#include "QskGraphic.h"
class QSK_EXPORT QskTextField : public QskTextInput class QSK_EXPORT QskTextField : public QskTextInput
{ {
Q_OBJECT 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 Q_PROPERTY( QString placeholderText READ placeholderText
WRITE setPlaceholderText NOTIFY placeholderTextChanged ) WRITE setPlaceholderText NOTIFY placeholderTextChanged )
using Inherited = QskTextInput; using Inherited = QskTextInput;
public: public:
QSK_SUBCONTROLS( Panel, Placeholder ) QSK_STATES( Pressed )
QSK_SUBCONTROLS( Panel, Header, Footer, Placeholder,
Icon, Button, ButtonPanel, CharacterCount )
QskTextField( QQuickItem* parent = nullptr ); QskTextField( QQuickItem* parent = nullptr );
QskTextField( const QString& text, QQuickItem* parent = nullptr ); QskTextField( const QString& text, QQuickItem* parent = nullptr );
~QskTextField() override; ~QskTextField() override;
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& ); void setPlaceholderText( const QString& );
QString placeholderText() const; QString placeholderText() const;
#if 1
QskAspect::States buttonStates() const;
#endif
Q_SIGNALS: Q_SIGNALS:
void headerTextChanged( const QString& );
void footerTextChanged( const QString& );
void placeholderTextChanged( const QString& ); void placeholderTextChanged( const QString& );
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: private:
void setButtonState( QskAspect::State, bool );
class PrivateData; class PrivateData;
std::unique_ptr< PrivateData > m_data; std::unique_ptr< PrivateData > m_data;
}; };

View File

@ -5,82 +5,335 @@
#include "QskTextFieldSkinlet.h" #include "QskTextFieldSkinlet.h"
#include "QskTextField.h" #include "QskTextField.h"
#include "QskFunctions.h"
#include "QskBoxHints.h"
#include <qfontmetrics.h>
using Q = QskTextField; using Q = QskTextField;
static QString qskEffectiveCharacterCountText( const QskTextField* textField )
{
auto policy = textField->flagHint< Qsk::Policy >(
Q::CharacterCount | QskAspect::Option, Qsk::Never );
const auto maxLength = textField->maxLength();
const bool isLimited = maxLength < 32767; // magic number hardcoded in qquicktextinput.cpp
if ( policy == Qsk::Always || ( policy == Qsk::Maybe && isLimited ) )
{
auto s = QString::number( textField->text().length() );
if ( isLimited )
s += " / " + QString::number( maxLength );
return s;
}
return QString();
}
QskTextFieldSkinlet::QskTextFieldSkinlet( QskSkin* skin ) QskTextFieldSkinlet::QskTextFieldSkinlet( QskSkin* skin )
: Inherited( skin ) : Inherited( skin )
{ {
setNodeRoles( { PanelRole, TextPanelRole, PlaceholderRole } ); setNodeRoles( { TextPanelRole, IconRole, ButtonPanelRole, ButtonRole,
PlaceholderRole, HeaderRole, FooterRole, CharacterCountRole } );
} }
QskTextFieldSkinlet::~QskTextFieldSkinlet() QskTextFieldSkinlet::~QskTextFieldSkinlet()
{ {
} }
void QskTextFieldSkinlet::setRenderHints( RenderHints hints )
{
m_renderHints = hints;
}
QskTextFieldSkinlet::RenderHints QskTextFieldSkinlet::renderHints() const
{
return m_renderHints;
}
QRectF QskTextFieldSkinlet::subControlRect( const QskSkinnable* skinnable, QRectF QskTextFieldSkinlet::subControlRect( const QskSkinnable* skinnable,
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
{ {
const auto textField = static_cast< const QskTextField* >( skinnable );
if ( subControl == Q::Panel ) if ( subControl == Q::Panel )
return contentsRect; return contentsRect;
if ( subControl == Q::Header )
return headerRect( skinnable, contentsRect );
if ( subControl == Q::Footer )
return alignedLabelRect( skinnable, contentsRect, subControl, Qt::AlignBottom );
if ( subControl == Q::CharacterCount )
return alignedLabelRect( skinnable, contentsRect, subControl, Qt::AlignBottom );
if ( subControl == Q::TextPanel ) if ( subControl == Q::TextPanel )
return skinnable->subControlContentsRect( contentsRect, Q::Panel ); return inputPanelRect( skinnable, contentsRect );
if ( subControl == Q::Text ) if ( subControl == Q::Text )
return skinnable->subControlContentsRect( contentsRect, Q::TextPanel ); {
auto rect = textField->subControlContentsRect( contentsRect, Q::TextPanel );
const auto iconRect = subControlRect( skinnable, contentsRect, Q::Icon );
if ( !iconRect.isEmpty() )
rect.setLeft( iconRect.right() );
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 );
return rect;
}
if ( subControl == Q::Placeholder ) if ( subControl == Q::Placeholder )
{ {
const auto textField = static_cast< const QskTextField* >( skinnable ); if ( hasText( skinnable, Q::Placeholder ) )
if( textField->text().isEmpty() )
return subControlRect( skinnable, contentsRect, Q::Text ); return subControlRect( skinnable, contentsRect, Q::Text );
return QRectF(); return QRectF();
} }
if ( subControl == Q::Icon )
{
if( !skinnable->symbolHint( subControl ).isEmpty() )
{
const auto rect = textField->subControlContentsRect( contentsRect, Q::TextPanel );
return qskAlignedRectF( rect,
skinnable->strutSizeHint( Q::Icon ), Qt::AlignLeft | Qt::AlignVCenter );
}
return QRectF();
}
if ( subControl == Q::ButtonPanel )
{
if ( textField->buttonStates() & Q::Hovered )
{
const auto sz = skinnable->strutSizeHint( Q::ButtonPanel );
if ( !sz.isEmpty() )
{
const auto r = subControlRect( skinnable, contentsRect, Q::Button );
QRectF rect( QPointF(), sz );
rect.moveCenter( r.center() );
return rect;
}
}
return QRectF();
}
if ( subControl == Q::Button )
{
if( !skinnable->symbolHint( Q::Button ).isEmpty() )
{
const auto rect = textField->subControlContentsRect( contentsRect, Q::TextPanel );
return qskAlignedRectF( rect,
skinnable->strutSizeHint( Q::Icon ), Qt::AlignRight | Qt::AlignVCenter );
}
return QRectF();
}
return Inherited::subControlRect( skinnable, contentsRect, subControl ); return Inherited::subControlRect( skinnable, contentsRect, subControl );
} }
QSGNode* QskTextFieldSkinlet::updateSubNode( QSGNode* QskTextFieldSkinlet::updateSubNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
{ {
const auto textField = static_cast< const QskTextField* >( skinnable );
switch ( nodeRole ) switch ( nodeRole )
{ {
case PanelRole:
{
return updateBoxNode( skinnable, node, Q::Panel );
}
case TextPanelRole: case TextPanelRole:
{ return updateInputPanelNode( skinnable, node );
return updateBoxNode( skinnable, node, Q::TextPanel );
}
case PlaceholderRole: case PlaceholderRole:
{ return updateLabelNode( skinnable, node, Q::Placeholder );
const auto text = effectivePlaceholderText( textField );
if ( text.isEmpty() )
return nullptr;
const auto subControl = Q::Placeholder; case HeaderRole:
return updateLabelNode( skinnable, node, Q::Header );
QskSkinHintStatus status; case FooterRole:
return updateLabelNode( skinnable, node, Q::Footer );
auto options = skinnable->textOptionsHint( subControl, &status ); case CharacterCountRole:
if ( !status.isValid() ) return updateLabelNode( skinnable, node, Q::CharacterCount );
options.setElideMode( Qt::ElideRight );
return updateTextNode( skinnable, node, case IconRole:
textField->subControlRect( subControl ), return updateSymbolNode( skinnable, node, Q::Icon );
textField->alignmentHint( subControl, Qt::AlignLeft ),
options, text, subControl ); case ButtonPanelRole:
} return updateBoxNode( skinnable, node, Q::ButtonPanel );
case ButtonRole:
return updateSymbolNode( skinnable, node, Q::Button );
} }
return Inherited::updateSubNode( skinnable, nodeRole, node ); return Inherited::updateSubNode( skinnable, nodeRole, node );
} }
QSGNode* QskTextFieldSkinlet::updateInputPanelNode(
const QskSkinnable* skinnable, QSGNode* node ) const
{
const auto control = static_cast< const QskControl* >( skinnable );
const auto rect = control->subControlRect( Q::TextPanel );
auto hints = skinnable->boxHints( Q::TextPanel );
if ( panelMode( skinnable ) == 2 )
{
const auto clipRect = control->subControlRect( Q::Header );
if ( !clipRect.isEmpty() )
{
// "cutting a hole" in the upper gradient for the header
const auto margin = 6; // ->skin
auto s1 = ( clipRect.left() - margin - rect.left() ) / rect.width();
auto s2 = ( clipRect.right() - rect.left() ) / rect.width();
auto gradient = hints.borderColors.top();
gradient.setStops( qskClippedGradientStops( gradient.stops(), s1, s2 ) );
hints.borderColors.setTop( gradient );
}
}
return updateBoxNode( skinnable, node, rect, hints );
}
QSGNode* QskTextFieldSkinlet::updateLabelNode( const QskSkinnable* skinnable,
QSGNode* node, QskAspect::Subcontrol subControl ) const
{
const auto text = effectiveText( skinnable, subControl );
if ( text.isEmpty() )
return nullptr;
QskSkinHintStatus status;
auto options = skinnable->textOptionsHint( subControl, &status );
if ( !status.isValid() )
options.setElideMode( Qt::ElideRight );
auto rect = skinnable->controlCast()->contentsRect();
rect = subControlRect( skinnable, rect, subControl );
return updateTextNode( skinnable, node, rect,
skinnable->alignmentHint( subControl, Qt::AlignLeft ),
options, text, subControl );
}
QRectF QskTextFieldSkinlet::headerRect(
const QskSkinnable* skinnable, const QRectF& contentsRect ) const
{
switch( panelMode( skinnable ) )
{
case 1:
{
const auto sz = effectiveTextSize( skinnable, Q::Header );
const auto r = subControlRect( skinnable, contentsRect, Q::Text );
return QRectF( r.x(), r.top() - sz.height(), sz.width(), sz.height() );
}
case 2:
{
const auto sz = effectiveTextSize( skinnable, Q::Header );
const auto r = subControlRect( skinnable, contentsRect, Q::TextPanel );
const auto x = r.left() + skinnable->paddingHint( Q::TextPanel ).left();
const auto y = r.top() - 0.5 * sz.height();
return QRectF( x, y, sz.width(), sz.height() );
}
default:
return alignedLabelRect( skinnable, contentsRect, Q::Header, Qt::AlignTop );
}
}
QRectF QskTextFieldSkinlet::inputPanelRect(
const QskSkinnable* skinnable, const QRectF& contentsRect ) const
{
auto rect = skinnable->subControlContentsRect( contentsRect, Q::Panel );
const auto mode = panelMode( skinnable );
if ( mode == 0 )
{
qreal h = skinnable->effectiveFontHeight( Q::Text );
h = skinnable->outerBoxSize( Q::TextPanel, QSizeF( rect.width(), h ) ).height();
h = qMax( h, skinnable->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 = textHeight( skinnable, Q::Header );
auto bottom = textHeight( skinnable, Q::Footer );
if ( rect.height() < top + h + bottom )
{
if ( effectiveText( skinnable, Q::Footer ).isEmpty() )
bottom = 0.0;
}
if ( rect.height() < top + h + bottom )
{
if ( effectiveText( skinnable, Q::Header ).isEmpty() )
top = 0.0;
}
return QRectF( rect.left(), rect.top() + top, rect.width(), h );
}
else
{
if ( mode == 2 )
{
qreal h = 0.0;
#if 0
h = effectiveTextHeight( skinnable, Q::Header );
#else
if ( !text( skinnable, Q::Header ).isEmpty() )
h = skinnable->effectiveFontHeight( Q::Header );
#endif
rect.setTop( rect.top() + 0.5 * h );
}
const auto h = skinnable->strutSizeHint( Q::TextPanel ).height();
rect.setHeight( h );
return rect;
}
}
QRectF QskTextFieldSkinlet::alignedLabelRect( const QskSkinnable* skinnable,
const QRectF& contentsRect, QskAspect::Subcontrol subControl,
Qt::Alignment alignment ) const
{
const auto h = effectiveTextHeight( skinnable, subControl );
if ( h <= 0.0 )
return QRectF();
const auto m = skinnable->marginHint( subControl );
auto r = subControlRect( skinnable, contentsRect, Q::TextPanel );
const auto y = ( alignment & Qt::AlignTop )
? r.top() - m.bottom() - h : r.bottom() + m.top();
return QRectF( r.left() + m.left(), y,
r.width() - m.left() - m.right(), h );
}
QSizeF QskTextFieldSkinlet::sizeHint( const QskSkinnable* skinnable, QSizeF QskTextFieldSkinlet::sizeHint( const QskSkinnable* skinnable,
Qt::SizeHint which, const QSizeF& constraint ) const Qt::SizeHint which, const QSizeF& constraint ) const
{ {
@ -89,30 +342,164 @@ QSizeF QskTextFieldSkinlet::sizeHint( const QskSkinnable* skinnable,
if ( which != Qt::PreferredSize ) if ( which != Qt::PreferredSize )
return QSizeF(); return QSizeF();
const auto input = static_cast< const QskAbstractTextInput* >( skinnable ); const auto textField = static_cast< const QskTextField* >( skinnable );
auto hint = input->unwrappedTextSize(); auto hint = textField->unwrappedTextSize();
hint = hint.grownBy( skinnable->marginHint( Q::Text ) ); hint = hint.expandedTo( skinnable->strutSizeHint( Q::Text ) );
hint = input->outerBoxSize( Q::TextPanel, hint ); if( !skinnable->symbolHint( Q::Icon ).isEmpty() )
hint = hint.expandedTo( input->strutSizeHint( Q::TextPanel ) ); {
const auto sz = skinnable->strutSizeHint( Q::Icon );
if ( sz.width() > 0.0 )
hint.rwidth() += sz.width();
hint.rheight() = qMax( hint.height(), sz.height() );
}
if( !skinnable->symbolHint( Q::Button ).isEmpty() )
{
const auto sz = skinnable->strutSizeHint( Q::Button );
if ( sz.width() > 0.0 )
hint.rwidth() += sz.width();
hint.rheight() = qMax( hint.height(), sz.height() );
}
switch( panelMode( skinnable ) )
{
case 0:
{
hint = skinnable->outerBoxSize( Q::TextPanel, hint );
hint = hint.expandedTo( skinnable->strutSizeHint( Q::TextPanel ) );
hint.rheight() += effectiveTextHeight( skinnable, Q::Header );
break;
}
case 1:
{
hint = hint.expandedTo( skinnable->strutSizeHint( Q::TextPanel ) );
break;
}
case 2:
{
hint = hint.expandedTo( skinnable->strutSizeHint( Q::TextPanel ) );
hint.rheight() += 0.5 * skinnable->effectiveFontHeight( Q::Header );
break;
}
}
hint.rheight() += qMax(
effectiveTextHeight( skinnable, Q::Footer ),
effectiveTextHeight( skinnable, Q::CharacterCount )
);
hint = skinnable->outerBoxSize( Q::Panel, hint ); hint = skinnable->outerBoxSize( Q::Panel, hint );
hint = hint.expandedTo( skinnable->strutSizeHint( Q::Panel ) ); return hint.expandedTo( skinnable->strutSizeHint( Q::Panel ) );
return hint;
} }
QString QskTextFieldSkinlet::effectivePlaceholderText( qreal QskTextFieldSkinlet::textHeight( const QskSkinnable* skinnable,
const QskTextField* textField ) const QskAspect::Subcontrol subControl ) const
{ {
if ( textField->text().isEmpty() && auto h = skinnable->effectiveFontHeight( subControl );
!( textField->isReadOnly() || textField->isEditing() ) )
{ const auto margins = skinnable->marginHint( subControl );
h += margins.top() + margins.bottom();
const auto sz = skinnable->strutSizeHint( subControl );
return qMax( h, sz.height() );
}
QSizeF QskTextFieldSkinlet::effectiveTextSize( const QskSkinnable* skinnable,
QskAspect::Subcontrol subControl ) const
{
const auto text = effectiveText( skinnable, subControl );
const QFontMetricsF fm( skinnable->effectiveFont( subControl ) );
auto w = qskHorizontalAdvance( fm, effectiveText( skinnable, subControl ) );
auto h = fm.height();
const auto margins = skinnable->marginHint( subControl );
w += margins.left() + margins.right();
h += margins.top() + margins.bottom();
QSizeF sz( w, h );
sz = sz.expandedTo( skinnable->strutSizeHint( subControl ) );
return sz;
}
QString QskTextFieldSkinlet::text(
const QskSkinnable* skinnable, QskAspect::Subcontrol subControl ) const
{
const auto textField = static_cast< const QskTextField* >( skinnable );
if ( subControl == Q::Text )
return textField->text();
if ( subControl == Q::Placeholder )
return textField->placeholderText(); return textField->placeholderText();
}
if ( subControl == Q::Header )
return textField->headerText();
if ( subControl == Q::Footer )
return textField->footerText();
if ( subControl == Q::CharacterCount )
return qskEffectiveCharacterCountText( textField );
return QString(); return QString();
} }
bool QskTextFieldSkinlet::isPlaceholderVisible(
const QskSkinnable* skinnable ) const
{
return !skinnable->hasSkinState( Q::Editing )
&& text( skinnable, Q::Text ).isEmpty();
}
bool QskTextFieldSkinlet::hasText(
const QskSkinnable* skinnable, QskAspect::Subcontrol subControl ) const
{
return !effectiveText( skinnable, subControl ).isEmpty();
}
QString QskTextFieldSkinlet::effectiveText(
const QskSkinnable* skinnable, QskAspect::Subcontrol subControl ) const
{
if ( subControl == Q::Placeholder )
{
if ( !isPlaceholderVisible( skinnable ) )
return QString();
auto txt = text( skinnable, Q::Placeholder );
if ( txt.isEmpty() && ( renderHints() & UseHeaderAsPlaceholder ) )
txt = text( skinnable, Q::Header );
return txt;
}
if ( subControl == Q::Header )
{
if ( isPlaceholderVisible( skinnable )
&& ( renderHints() & UseHeaderAsPlaceholder ) )
{
return QString();
}
return text( skinnable, Q::Header );
}
return text( skinnable, subControl );
}
qreal QskTextFieldSkinlet::effectiveTextHeight(
const QskSkinnable* skinnable, QskAspect::Subcontrol subcontrol ) const
{
const auto text = effectiveText( skinnable, subcontrol );
return text.isEmpty() ? 0.0 : textHeight( skinnable, subcontrol );
}
#include "moc_QskTextFieldSkinlet.cpp" #include "moc_QskTextFieldSkinlet.cpp"

View File

@ -19,15 +19,34 @@ class QSK_EXPORT QskTextFieldSkinlet : public QskSkinlet
public: public:
enum NodeRole : quint8 enum NodeRole : quint8
{ {
PanelRole,
TextPanelRole, TextPanelRole,
HeaderRole,
FooterRole,
CharacterCountRole,
PlaceholderRole, PlaceholderRole,
IconRole,
ButtonPanelRole,
ButtonRole,
RoleCount RoleCount
}; };
enum RenderHint
{
UseHeaderAsPlaceholder = 1 << 0
};
Q_ENUM( RenderHint )
Q_DECLARE_FLAGS( RenderHints, RenderHint )
Q_INVOKABLE QskTextFieldSkinlet( QskSkin* = nullptr ); Q_INVOKABLE QskTextFieldSkinlet( QskSkin* = nullptr );
~QskTextFieldSkinlet() override; ~QskTextFieldSkinlet() override;
void setRenderHints( RenderHints );
RenderHints renderHints() const;
QRectF subControlRect( const QskSkinnable*, QRectF subControlRect( const QskSkinnable*,
const QRectF& rect, QskAspect::Subcontrol ) const override; const QRectF& rect, QskAspect::Subcontrol ) const override;
@ -38,7 +57,35 @@ class QSK_EXPORT QskTextFieldSkinlet : public QskSkinlet
QSGNode* updateSubNode( const QskSkinnable*, QSGNode* updateSubNode( const QskSkinnable*,
quint8 nodeRole, QSGNode* ) const override; quint8 nodeRole, QSGNode* ) const override;
virtual QString effectivePlaceholderText( const QskTextField* ) const; QSGNode* updateLabelNode( const QskSkinnable*,
QSGNode*, QskAspect::Subcontrol ) const;
QString effectiveText( const QskSkinnable*,
QskAspect::Subcontrol ) const;
QString text( const QskSkinnable*, QskAspect::Subcontrol ) const;
qreal textHeight( const QskSkinnable*, QskAspect::Subcontrol ) const;
bool hasText( const QskSkinnable*, QskAspect::Subcontrol ) const;
QSizeF effectiveTextSize( const QskSkinnable*, QskAspect::Subcontrol ) const;
qreal effectiveTextHeight( const QskSkinnable*, QskAspect::Subcontrol ) const;
private:
QRectF headerRect( const QskSkinnable*, const QRectF& ) const;
QRectF inputPanelRect( const QskSkinnable*, const QRectF& ) const;
QSGNode* updateInputPanelNode( const QskSkinnable*, QSGNode* ) const;
QRectF alignedLabelRect( const QskSkinnable*, const QRectF&,
QskAspect::Subcontrol, Qt::Alignment ) const;
bool isPlaceholderVisible( const QskSkinnable* ) const;
virtual int panelMode( const QskSkinnable* ) const { return 0; }
RenderHints m_renderHints;
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS( QskTextFieldSkinlet::RenderHints )
#endif #endif

View File

@ -161,7 +161,7 @@ QskTextInput::QskTextInput( QQuickItem* parent )
setAcceptedMouseButtons( wrappedInput->acceptedMouseButtons() ); setAcceptedMouseButtons( wrappedInput->acceptedMouseButtons() );
wrappedInput->setAcceptedMouseButtons( Qt::NoButton ); wrappedInput->setAcceptedMouseButtons( Qt::NoButton );
initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Fixed ); initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Minimum );
setup( wrappedInput ); setup( wrappedInput );