From 0c2bc32abd1f568d3a5481074fb4dba964beeec3 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Sun, 19 Feb 2023 14:24:09 +0100 Subject: [PATCH] focus handling removed ( spin boxes do not have an internal focus chain ), formal adjustments --- doc/classes/QskSpinBox.dox | 59 +++ doc/classes/QskSpinBoxSkinlet.dox | 20 + examples/gallery/spinbox/SpinBoxPage.cpp | 2 +- skins/material3/QskMaterial3Skin.cpp | 219 +++++------ src/controls/QskSpinBox.cpp | 451 +++++------------------ src/controls/QskSpinBox.h | 67 +--- src/controls/QskSpinBoxSkinlet.cpp | 228 ++++++------ src/controls/QskSpinBoxSkinlet.h | 97 +---- 8 files changed, 430 insertions(+), 713 deletions(-) create mode 100644 doc/classes/QskSpinBox.dox create mode 100644 doc/classes/QskSpinBoxSkinlet.dox diff --git a/doc/classes/QskSpinBox.dox b/doc/classes/QskSpinBox.dox new file mode 100644 index 00000000..8fbdf4de --- /dev/null +++ b/doc/classes/QskSpinBox.dox @@ -0,0 +1,59 @@ +/*! + \class QskSpinBox QskSpinBox.h + + \brief A control to edit, increment and decrement number values + + QskSpinBox allows the user to choose a value by: + + - clicking the up and down buttons + - pressing Up or Down on the keyboard + - using the wheel + - typing in the value manually + + \subcontrols QskSpinBox::Panel, QskSpinBox::TextPanel, + QskSpinBox::IncrementPanel, QskSpinBox::DecrementPanel, + QskSpinBox::IncrementText, QskSpinBox::DecrementText + + \states QskSpinBox::Pressed + + \skinlet QskSpinBoxSkinlet +*/ + +/*! + \var QskSpinBox::IncrementPanel + + \brief The background of the increment button + \sa QskSpinBoxSkinlet + */ + +/*! + \var QskSpinBox::DecrementPanel + + \brief The background of the decrement button + \sa QskSpinBoxSkinlet + */ + +/*! + \var QskSpinBox::TextPanel + + \brief The background of the text showing the number + \sa QskSpinBoxSkinlet + */ + +/*! + \var QskSpinBox::Text + + \brief The text showing the number + \sa QskSpinBoxSkinlet + */ + +/*! + \fn QskSpinBox::QskSpinBox( QQuickItem* ) + + Constructs a spin box with the given parent. +*/ + +/*! + \fn QskSeparator::~QskSeparator(); + Destroys this spin box. +*/ diff --git a/doc/classes/QskSpinBoxSkinlet.dox b/doc/classes/QskSpinBoxSkinlet.dox new file mode 100644 index 00000000..69f96f5a --- /dev/null +++ b/doc/classes/QskSpinBoxSkinlet.dox @@ -0,0 +1,20 @@ +/*! + \class QskSpinBoxSkinlet QskSpinBoxSkinlet.h + + \brief This skinlet's purpose is to draw a QskSpinBox instance + + In order to manage individual subcontrol states this skinlet uses + subcontrol sampling. Although it is most usefull when dealing with + dynamic or large numbers of subcontrols, it is a strategy to index + the subcontrol in order to have individual states instead of one + collective state on the skinnable object. + + \note The placement and dimensions of all subcontrols depend on the + following subctrontrol aspects: + + - QskSpinBox::Layout's alignment hint ( which affects the positions of all controls ) + - QskSpinBox::Layout's spacing hint + - QskSpinBox::IncrementPanel's strut size hint + - QskSpinBox::DecrementPanel's strut size hint + - QskSpinBox::TextPanel's strut size hint +*/ diff --git a/examples/gallery/spinbox/SpinBoxPage.cpp b/examples/gallery/spinbox/SpinBoxPage.cpp index 7ef8b99e..6610670e 100644 --- a/examples/gallery/spinbox/SpinBoxPage.cpp +++ b/examples/gallery/spinbox/SpinBoxPage.cpp @@ -46,7 +46,7 @@ void SpinBoxPage::populate() auto* const column = new QskLinearBox( Qt::Vertical, grid ); auto* const label = new QskTextLabel( layouts.value( layout ), column ); auto* const spinbox = new QskSpinBox( column ); - spinbox->setAlignmentHint( QskSpinBox::Layout, layout ); + spinbox->setAlignmentHint( QskSpinBox::Panel, layout ); grid->addItem( column, y, x ); column->setStretchFactor( label, 1 ); column->setStretchFactor( spinbox, 99 ); diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index 4ed9b714..6265185f 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -156,7 +156,8 @@ namespace const QskMaterial3Theme& m_pal; }; - QFont createFont( const QString& name, int lineHeight, int size, qreal tracking, QFont::Weight weight ) + QFont createFont( const QString& name, int lineHeight, + int size, qreal tracking, QFont::Weight weight ) { QFont font( name, size ); font.setPixelSize( lineHeight ); @@ -244,7 +245,7 @@ void Editor::setupCheckBox() setGraphicRole( Q::Indicator | Q::Error, QskMaterial3Skin::GraphicRoleOnError ); - setStrutSize( Q::Ripple, { 40_dp, 40_dp } ); + setStrutSize( Q::Ripple, 40_dp, 40_dp ); setBoxShape( Q::Ripple, 100, Qt::RelativeSize ); setGradient( Q::Ripple, Qt::transparent ); @@ -289,7 +290,7 @@ void Editor::setupComboBox() { using Q = QskComboBox; - setStrutSize( Q::Panel, { -1, 56_dp } ); + 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 ); @@ -297,22 +298,25 @@ void Editor::setupComboBox() setBoxBorderColors( Q::Panel, m_pal.onSurfaceVariant ); setSpacing( Q::Panel, 8_dp ); - const auto hoverColor = flattenedColor( m_pal.onSurfaceVariant, m_pal.surfaceVariant, m_pal.hoverOpacity ); + const auto hoverColor = flattenedColor( m_pal.onSurfaceVariant, + m_pal.surfaceVariant, m_pal.hoverOpacity ); setGradient( Q::Panel | Q::Hovered, hoverColor ); - const auto focusColor = flattenedColor( m_pal.onSurfaceVariant, m_pal.surfaceVariant, m_pal.focusOpacity ); + const auto focusColor = flattenedColor( m_pal.onSurfaceVariant, + m_pal.surfaceVariant, m_pal.focusOpacity ); setGradient( Q::Panel | Q::Focused, focusColor ); - const auto pressedColor = flattenedColor( m_pal.onSurfaceVariant, m_pal.surfaceVariant, m_pal.pressedOpacity ); + const auto pressedColor = flattenedColor( m_pal.onSurfaceVariant, + m_pal.surfaceVariant, m_pal.pressedOpacity ); setGradient( Q::Panel | Q::Pressed, pressedColor ); - setStrutSize( Q::Graphic, { 24_dp, 24_dp } ); + setStrutSize( Q::Graphic, 24_dp, 24_dp ); setGraphicRole( Q::Graphic, QskMaterial3Skin::GraphicRoleOnSurface ); setColor( Q::Text, m_pal.onSurface ); setFontRole( Q::Text, QskMaterial3Skin::M3BodyMedium ); - setStrutSize( Q::OpenMenuGraphic, { 12_dp, 12_dp } ); + setStrutSize( Q::OpenMenuGraphic, 12_dp, 12_dp ); setGraphicRole( Q::OpenMenuGraphic, QskMaterial3Skin::GraphicRoleOnSurface ); setAlignment( Q::OpenMenuGraphic, Qt::AlignRight | Qt::AlignVCenter ); @@ -378,7 +382,7 @@ void Editor::setupMenu() setGradient( Q::Cursor, m_pal.primary12 ); setPadding( Q::Graphic, 7_dp ); - setStrutSize( Q::Graphic, { 24_dp, 24_dp } ); + setStrutSize( Q::Graphic, 24_dp, 24_dp ); setGraphicRole( Q::Graphic, QskMaterial3Skin::GraphicRoleOnSurface ); setColor( Q::Text, m_pal.onSurface ); @@ -516,18 +520,20 @@ void Editor::setupSegmentedBar() setBoxShape( Q::Cursor, 0 ); setBoxShape( Q::Cursor | Q::Minimum | A::Horizontal, - { 100, 0, 100, 0, Qt::RelativeSize }, - { QskStateCombination::CombinationNoState, Q::Disabled } ); + { 100, 0, 100, 0, Qt::RelativeSize }, + { QskStateCombination::CombinationNoState, Q::Disabled } ); + setBoxShape( Q::Cursor | Q::Maximum | A::Horizontal, - { 0, 100, 0, 100, Qt::RelativeSize }, - { QskStateCombination::CombinationNoState, Q::Disabled } ); + { 0, 100, 0, 100, Qt::RelativeSize }, + { QskStateCombination::CombinationNoState, Q::Disabled } ); setBoxShape( Q::Cursor | Q::Minimum | A::Vertical, - { 100, 100, 0, 0, Qt::RelativeSize }, - { QskStateCombination::CombinationNoState, Q::Disabled } ); + { 100, 100, 0, 0, Qt::RelativeSize }, + { QskStateCombination::CombinationNoState, Q::Disabled } ); + setBoxShape( Q::Cursor | Q::Maximum | A::Vertical, - { 0, 0, 100, 100, Qt::RelativeSize }, - { QskStateCombination::CombinationNoState, Q::Disabled } ); + { 0, 0, 100, 100, Qt::RelativeSize }, + { QskStateCombination::CombinationNoState, Q::Disabled } ); setGradient( Q::Cursor, m_pal.secondaryContainer ); setGradient( Q::Cursor | Q::Disabled, m_pal.onSurface12 ); @@ -550,10 +556,11 @@ void Editor::setupSegmentedBar() // Graphic setPadding( Q::Graphic, 0_dp ); - setStrutSize( Q::Graphic, { 18_dp, 18_dp } ); + setStrutSize( Q::Graphic, 18_dp, 18_dp ); setGraphicRole( Q::Graphic, QskMaterial3Skin::GraphicRoleOnSurface ); - setGraphicRole( Q::Graphic | Q::Selected, QskMaterial3Skin::GraphicRoleOnSecondaryContainer ); + setGraphicRole( Q::Graphic | Q::Selected, + QskMaterial3Skin::GraphicRoleOnSecondaryContainer ); setGraphicRole( Q::Graphic | Q::Disabled, QskMaterial3Skin::GraphicRoleOnSurface38 ); } } @@ -691,7 +698,7 @@ void Editor::setupSlider() setBoxShape( Q::Handle, 100, Qt::RelativeSize ); setBoxBorderMetrics( Q::Handle, 0 ); - setStrutSize( Q::Handle, { 20_dp, 20_dp } ); + setStrutSize( Q::Handle, 20_dp, 20_dp ); setGradient( Q::Handle, m_pal.primary ); setGradient( Q::Handle | Q::Pressed, m_pal.primary ); @@ -699,7 +706,7 @@ void Editor::setupSlider() const auto disabledColor = flattenedColor( m_pal.onSurface, m_pal.background, 0.38 ); setGradient( Q::Handle | Q::Disabled, disabledColor ); - setStrutSize( Q::Ripple, { 40_dp, 40_dp } ); + setStrutSize( Q::Ripple, 40_dp, 40_dp ); setBoxShape( Q::Ripple, 100, Qt::RelativeSize ); setGradient( Q::Ripple, Qt::transparent ); setGradient( Q::Ripple | Q::Hovered, m_pal.primary12 ); @@ -712,71 +719,71 @@ void Editor::setupSlider() void Editor::setupSpinBox() { - using Q = QskSpinBox; + using Q = QskSpinBox; - setSpacing(QskSpinBox::Layout, 4_dp); + setSpacing( Q::Panel, 4_dp ); - setStrutSize(QskSpinBox::TextPanel | QskAspect::Size, {80_dp,40_dp}); - setStrutSize(QskSpinBox::IncrementPanel | QskAspect::Size, {40_dp,40_dp}); - setStrutSize(QskSpinBox::DecrementPanel | QskAspect::Size, {40_dp,40_dp}); + setStrutSize( Q::TextPanel, 80_dp, 40_dp ); + setStrutSize( Q::IncrementPanel, 40_dp,40_dp ); + setStrutSize( Q::DecrementPanel, 40_dp, 40_dp ); - setAlignment(QskSpinBox::Layout, Qt::AlignHCenter); - setAlignment(Q::Text, Qt::AlignCenter); + setAlignment( Q::Panel, Qt::AlignHCenter ); + setAlignment( Q::Text, Qt::AlignCenter ); - for(const auto& state : {QskSpinBox::DecrementPanel, QskSpinBox::IncrementPanel, QskSpinBox::TextPanel}) - { - setBoxShape(state, 4_dp); - setBoxBorderMetrics(state, 1_dp); - } + for( const auto& state : { Q::DecrementPanel, Q::IncrementPanel, Q::TextPanel } ) + { + setBoxShape( state, 4_dp ); + setBoxBorderMetrics( state, 1_dp ); + } - for(const auto& state : {QskSpinBox::DecrementPanel, QskSpinBox::IncrementPanel}) - { - setGradient( state, m_pal.primary ); - setGradient( state | Q::Disabled, m_pal.onSurface12 ); + for( const auto& state : { Q::DecrementPanel, Q::IncrementPanel } ) + { + setGradient( state, m_pal.primary ); + setGradient( state | Q::Disabled, m_pal.onSurface12 ); - const auto focusColor = flattenedColor( m_pal.onPrimary, m_pal.primary, 0.12 ); - setGradient( state | Q::Focused, focusColor ); - setGradient( state | Q::Pressed, focusColor ); + const auto focusColor = flattenedColor( m_pal.onPrimary, m_pal.primary, 0.12 ); + setGradient( state | Q::Focused, focusColor ); + setGradient( state | Q::Pressed, focusColor ); - const auto hoverColor = flattenedColor( m_pal.onPrimary, m_pal.primary, 0.08 ); - setGradient( state | Q::Hovered, hoverColor ); - setShadowMetrics( state | Q::Hovered, m_pal.elevationLight1 ); - setShadowColor( state | Q::Hovered, m_pal.shadow ); - } + const auto hoverColor = flattenedColor( m_pal.onPrimary, m_pal.primary, 0.08 ); + setGradient( state | Q::Hovered, hoverColor ); + setShadowMetrics( state | Q::Hovered, m_pal.elevationLight1 ); + setShadowColor( state | Q::Hovered, m_pal.shadow ); + } - for(const auto& state : {QskSpinBox::DecrementText, QskSpinBox::IncrementText}) - { - setColor( state, m_pal.onPrimary ); - setColor( state | Q::Disabled, m_pal.onSurface38 ); - setAlignment(state, Qt::AlignCenter); - setFontRole( state, QskMaterial3Skin::M3LabelLarge ); - } + for( const auto& state : { Q::DecrementIndicator, Q::IncrementIndicator } ) + { + setColor( state, m_pal.onPrimary ); + setColor( state | Q::Disabled, m_pal.onSurface38 ); + setAlignment(state, Qt::AlignCenter ); + setFontRole( state, QskMaterial3Skin::M3LabelLarge ); + } - setColor( Q::Text, m_pal.onBackground ); - setColor( Q::Text | Q::Disabled, m_pal.onSurface38 ); + setColor( Q::Text, m_pal.onBackground ); + setColor( Q::Text | Q::Disabled, m_pal.onSurface38 ); - setPadding( Q::TextPanel, 5_dp ); - setBoxShape( Q::TextPanel, 4_dp, 4_dp, 0, 0 ); - setBoxBorderMetrics( Q::TextPanel, 0, 0, 0, 1_dp ); - setBoxBorderColors( Q::TextPanel, m_pal.onSurface ); + setPadding( Q::TextPanel, 5_dp ); + setBoxShape( Q::TextPanel, 4_dp, 4_dp, 0, 0 ); + setBoxBorderMetrics( Q::TextPanel, 0, 0, 0, 1_dp ); + setBoxBorderColors( Q::TextPanel, m_pal.onSurface ); - setBoxBorderMetrics( Q::TextPanel | Q::Focused, 0, 0, 0, 2_dp ); - setBoxBorderColors( Q::TextPanel | Q::Focused, m_pal.primary ); + setBoxBorderMetrics( Q::TextPanel | Q::Focused, 0, 0, 0, 2_dp ); + setBoxBorderColors( Q::TextPanel | Q::Focused, m_pal.primary ); // setBoxBorderMetrics( Q::TextPanel | Q::Editing, 0, 0, 0, 2_dp ); // setBoxBorderColors( Q::TextPanel | Q::Editing, m_pal.primary ); - setBoxBorderMetrics( Q::TextPanel | Q::Hovered, 0, 0, 0, 1_dp ); - setBoxBorderColors( Q::TextPanel | Q::Hovered, m_pal.onSurface ); + setBoxBorderMetrics( Q::TextPanel | Q::Hovered, 0, 0, 0, 1_dp ); + setBoxBorderColors( Q::TextPanel | Q::Hovered, m_pal.onSurface ); - setGradient( Q::TextPanel, m_pal.surfaceVariant ); + setGradient( Q::TextPanel, m_pal.surfaceVariant ); - const auto c1 = QskRgb::toTransparentF( m_pal.onSurface, 0.04 ); - setGradient( Q::TextPanel | Q::Disabled, c1 ); - setBoxBorderMetrics( Q::TextPanel | Q::Disabled, 0, 0, 0, 1_dp ); + const auto c1 = QskRgb::toTransparentF( m_pal.onSurface, 0.04 ); + setGradient( Q::TextPanel | Q::Disabled, c1 ); + setBoxBorderMetrics( Q::TextPanel | Q::Disabled, 0, 0, 0, 1_dp ); - setColor( Q::TextPanel | Q::Disabled, m_pal.onSurface38 ); - setBoxBorderColors( Q::TextPanel | Q::Disabled, m_pal.onSurface38 ); + setColor( Q::TextPanel | Q::Disabled, m_pal.onSurface38 ); + setBoxBorderColors( Q::TextPanel | Q::Disabled, m_pal.onSurface38 ); } void Editor::setupSwitchButton() @@ -801,7 +808,8 @@ void Editor::setupSwitchButton() setBoxShape( Q::Handle, 100, Qt::RelativeSize ); setStrutSize( Q::Handle, 16_dp, 16_dp ); - setStrutSize( Q::Handle | Q::Checked, 24_dp, 24_dp, { QskStateCombination::CombinationNoState, Q::Disabled } ); + setStrutSize( Q::Handle | Q::Checked, 24_dp, 24_dp, + { QskStateCombination::CombinationNoState, Q::Disabled } ); setGradient( Q::Handle, m_pal.outline ); setGradient( Q::Handle | Q::Checked, m_pal.primaryContainer ); @@ -816,7 +824,8 @@ void Editor::setupSwitchButton() setStrutSize( Q::Ripple | Q::Hovered, 40_dp, 40_dp ); setBoxShape( Q::Ripple, 100, Qt::RelativeSize ); setGradient( Q::Ripple | Q::Hovered, stateLayerColor( m_pal.onSurface, m_pal.focusOpacity ) ); - setGradient( Q::Ripple | Q::Hovered | Q::Checked, stateLayerColor( m_pal.primary, m_pal.focusOpacity ) ); + setGradient( Q::Ripple | Q::Hovered | Q::Checked, + stateLayerColor( m_pal.primary, m_pal.focusOpacity ) ); setBoxBorderColors( Q::Handle, m_pal.outline ); setBoxBorderColors( Q::Handle | Q::Checked, m_pal.primary ); @@ -891,7 +900,7 @@ void Editor::setupTabButton() setGradient( Q::Panel | Q::Hovered, QskRgb::toTransparentF( m_pal.surface, m_pal.hoverOpacity ) ); - setGradient( Q::Panel | Q::Focused, + setGradient( Q::Panel | Q::Focused, QskRgb::toTransparentF( m_pal.surface, m_pal.focusOpacity ) ); setGradient( Q::Panel | Q::Pressed, @@ -899,12 +908,18 @@ void Editor::setupTabButton() setGradient( Q::Panel | A::Footer, m_pal.surface2 ); setGradient( Q::Panel | A::Footer | Q::Checked, m_pal.secondaryContainer ); - setGradient( Q::Panel | A::Footer | Q::Hovered, stateLayerColor( m_pal.onSurfaceVariant, m_pal.hoverOpacity ) ); - setGradient( Q::Panel | A::Footer | Q::Hovered | Q::Checked, stateLayerColor( m_pal.onSurface, m_pal.hoverOpacity ) ); - setGradient( Q::Panel | A::Footer | Q::Focused, stateLayerColor( m_pal.onSurfaceVariant, m_pal.focusOpacity ) ); - setGradient( Q::Panel | A::Footer | Q::Focused | Q::Checked, stateLayerColor( m_pal.onSurface, m_pal.focusOpacity ) ); - setGradient( Q::Panel | A::Footer | Q::Pressed, stateLayerColor( m_pal.onSurfaceVariant, m_pal.pressedOpacity ) ); - setGradient( Q::Panel | A::Footer | Q::Pressed | Q::Checked, stateLayerColor( m_pal.onSurface, m_pal.pressedOpacity ) ); + setGradient( Q::Panel | A::Footer | Q::Hovered, + stateLayerColor( m_pal.onSurfaceVariant, m_pal.hoverOpacity ) ); + setGradient( Q::Panel | A::Footer | Q::Hovered | Q::Checked, + stateLayerColor( m_pal.onSurface, m_pal.hoverOpacity ) ); + setGradient( Q::Panel | A::Footer | Q::Focused, + stateLayerColor( m_pal.onSurfaceVariant, m_pal.focusOpacity ) ); + setGradient( Q::Panel | A::Footer | Q::Focused | Q::Checked, + stateLayerColor( m_pal.onSurface, m_pal.focusOpacity ) ); + setGradient( Q::Panel | A::Footer | Q::Pressed, + stateLayerColor( m_pal.onSurfaceVariant, m_pal.pressedOpacity ) ); + setGradient( Q::Panel | A::Footer | Q::Pressed | Q::Checked, + stateLayerColor( m_pal.onSurface, m_pal.pressedOpacity ) ); setAnimation( Q::Panel | A::Color, qskDuration ); @@ -1066,7 +1081,7 @@ QskMaterial3Theme::QskMaterial3Theme( Lightness lightness ) } QskMaterial3Theme::QskMaterial3Theme( Lightness lightness, - std::array< QskHctColor, NumPaletteTypes > palettes ) + std::array< QskHctColor, NumPaletteTypes > palettes ) : m_palettes( palettes ) { if ( lightness == Light ) @@ -1196,28 +1211,23 @@ QskGraphic QskMaterial3Skin::symbol( int symbolType ) const switch ( symbolType ) { - case QskStandardSymbol::CheckMark: - { - return *( provider->requestGraphic( "check_small" ) ); - } - case QskStandardSymbol::CrossMark: - { - return {}; - } - case QskStandardSymbol::SegmentedBarCheckMark: - { - return *( provider->requestGraphic( "segmented-button-check" ) ); - } - case QskStandardSymbol::ComboBoxSymbolPopupClosed: - { - return *( provider->requestGraphic( "combo-box-arrow-closed" ) ); - } - case QskStandardSymbol::ComboBoxSymbolPopupOpen: - { - return *( provider->requestGraphic( "combo-box-arrow-open" ) ); - } - default: - return Inherited::symbol( symbolType ); + case QskStandardSymbol::CheckMark: + return *( provider->requestGraphic( "check_small" ) ); + + case QskStandardSymbol::CrossMark: + return {}; + + case QskStandardSymbol::SegmentedBarCheckMark: + return *( provider->requestGraphic( "segmented-button-check" ) ); + + case QskStandardSymbol::ComboBoxSymbolPopupClosed: + return *( provider->requestGraphic( "combo-box-arrow-closed" ) ); + + case QskStandardSymbol::ComboBoxSymbolPopupOpen: + return *( provider->requestGraphic( "combo-box-arrow-open" ) ); + + default: + return Inherited::symbol( symbolType ); } } @@ -1225,9 +1235,12 @@ void QskMaterial3Skin::setupFonts() { Inherited::setupFonts( QStringLiteral( "Roboto" ) ); - setFont( M3BodyMedium, createFont( QStringLiteral( "Roboto Regular"), 20_dp, 14_dp, 0.25, QFont::Normal ) ); - setFont( M3BodyLarge, createFont( QStringLiteral( "Roboto Medium" ), 24_dp, 16_dp, 0.5, QFont::Normal ) ); - setFont( M3HeadlineSmall, createFont( QStringLiteral( "Roboto Regular" ), 32_dp, 28_dp, 0.0, QFont::Normal ) ); + setFont( M3BodyMedium, + createFont( QStringLiteral( "Roboto Regular"), 20_dp, 14_dp, 0.25, QFont::Normal ) ); + setFont( M3BodyLarge, + createFont( QStringLiteral( "Roboto Medium" ), 24_dp, 16_dp, 0.5, QFont::Normal ) ); + setFont( M3HeadlineSmall, + createFont( QStringLiteral( "Roboto Regular" ), 32_dp, 28_dp, 0.0, QFont::Normal ) ); setFont( M3LabelLarge, createFont( "Roboto Medium", 20_dp, 14_dp, 0.1, QFont::Medium ) ); } diff --git a/src/controls/QskSpinBox.cpp b/src/controls/QskSpinBox.cpp index 8637cc40..d6f8a93d 100644 --- a/src/controls/QskSpinBox.cpp +++ b/src/controls/QskSpinBox.cpp @@ -7,454 +7,171 @@ #include "QskIntervalF.h" #include "QskEvent.h" -#include -#include -#include +QSK_SUBCONTROL( QskSpinBox, Panel ) -#include +QSK_SUBCONTROL( QskSpinBox, TextPanel ) +QSK_SUBCONTROL( QskSpinBox, Text ) QSK_SUBCONTROL( QskSpinBox, IncrementPanel ) +QSK_SUBCONTROL( QskSpinBox, IncrementIndicator ) + QSK_SUBCONTROL( QskSpinBox, DecrementPanel ) -QSK_SUBCONTROL( QskSpinBox, IncrementText ) -QSK_SUBCONTROL( QskSpinBox, DecrementText ) -QSK_SUBCONTROL( QskSpinBox, Text ) -QSK_SUBCONTROL( QskSpinBox, TextPanel ) -QSK_SUBCONTROL( QskSpinBox, Layout ) +QSK_SUBCONTROL( QskSpinBox, DecrementIndicator ) QSK_SYSTEM_STATE( QskSpinBox, Pressed, ( QskAspect::QskAspect::FirstSystemState << 0 ) ) -namespace aliased_enum +namespace { - constexpr auto D = QskSpinBox::Decrement; - constexpr auto T = QskSpinBox::Textbox; - constexpr auto I = QskSpinBox::Increment; - constexpr auto N = QskSpinBox::None; + enum + { + ButtonDecrement = -1, + ButtonNone = 0, + ButtonIncrement = 1 + }; + + inline int buttonAt( const QskSpinBox* spinBox, const QPointF& pos ) + { + if ( spinBox->subControlRect( QskSpinBox::IncrementPanel ).contains( pos ) ) + return ButtonIncrement; + + if ( spinBox->subControlRect( QskSpinBox::DecrementPanel ).contains( pos ) ) + return ButtonDecrement; + + return ButtonNone; + } } class QskSpinBox::PrivateData { public: - explicit PrivateData( QskSpinBox* const parent ) - : q( parent ) + inline void setPressed( QskSpinBox* spinBox, int button ) { - } - - FocusIndeces defaultFocusIndex() const - { - const auto layout = q->alignmentHint( QskSpinBox::Layout ); - - if ( layout == Qt::AlignLeft ) + if ( pressedButton != button ) { - return QskSpinBox::Textbox; - } - if ( layout == Qt::AlignRight ) - { - return QskSpinBox::Decrement; - } - if ( layout == Qt::AlignHCenter ) - { - return QskSpinBox::Decrement; - } - if ( layout == Qt::AlignTop ) - { - return QskSpinBox::Textbox; - } - if ( layout == Qt::AlignBottom ) - { - return QskSpinBox::Increment; - } - if ( layout == Qt::AlignVCenter ) - { - return QskSpinBox::Increment; - } - if ( layout == ( Qt::AlignLeft | Qt::AlignVCenter ) ) - { - return QskSpinBox::Textbox; - } - if ( layout == ( Qt::AlignRight | Qt::AlignVCenter ) ) - { - return QskSpinBox::Increment; - } - if ( layout == ( Qt::AlignTop | Qt::AlignHCenter ) ) - { - return QskSpinBox::Textbox; - } - if ( layout == ( Qt::AlignBottom | Qt::AlignHCenter ) ) - { - return QskSpinBox::Decrement; - } - - return None; - } - - FocusIndeces nextFocusIndex() const - { - const auto layout = q->alignmentHint( QskSpinBox::Layout ); - using namespace aliased_enum; - - // [0][1][2][3] := index - // [D][T][I][N] := control - using LUT = std::array< QskSpinBox::FocusIndeces, 4 >; - if ( layout == Qt::AlignLeft ) - { - return LUT{ I, D, N, T }[ m_focusIndex ]; - } - if ( layout == Qt::AlignRight ) - { - return LUT{ I, N, T, D }[ m_focusIndex ]; - } - if ( layout == Qt::AlignHCenter ) - { - return LUT{ T, I, N, D }[ m_focusIndex ]; - } - if ( layout == Qt::AlignTop ) - { - return LUT{ N, I, D, T }[ m_focusIndex ]; - } - if ( layout == Qt::AlignBottom ) - { - return LUT{ T, N, D, I }[ m_focusIndex ]; - } - if ( layout == Qt::AlignVCenter ) - { - return LUT{ N, D, T, I }[ m_focusIndex ]; - } - if ( layout == ( Qt::AlignLeft | Qt::AlignVCenter ) ) - { - return LUT{ N, I, D, T }[ m_focusIndex ]; - } - if ( layout == ( Qt::AlignRight | Qt::AlignVCenter ) ) - { - return LUT{ T, N, D, I }[ m_focusIndex ]; - } - if ( layout == ( Qt::AlignTop | Qt::AlignHCenter ) ) - { - return LUT{ I, D, N, T }[ m_focusIndex ]; - } - if ( layout == ( Qt::AlignBottom | Qt::AlignHCenter ) ) - { - return LUT{ I, N, T, D }[ m_focusIndex ]; - } - - return None; - } - - FocusIndeces previousFocusIndex() const - { - const auto layout = q->alignmentHint( QskSpinBox::Layout ); - using namespace aliased_enum; - - // [0][1][2][3] := index - // [D][T][I][N] := control - using LUT = std::array< FocusIndeces, 4 >; - if ( layout == Qt::AlignLeft ) - { - return LUT{ T, N, D, I }[ m_focusIndex ]; - } - if ( layout == Qt::AlignRight ) - { - return LUT{ N, I, D, T }[ m_focusIndex ]; - } - if ( layout == Qt::AlignHCenter ) - { - return LUT{ N, D, T, I }[ m_focusIndex ]; - } - if ( layout == Qt::AlignTop ) - { - return LUT{ I, N, T, D }[ m_focusIndex ]; - } - if ( layout == Qt::AlignBottom ) - { - return LUT{ I, D, N, T }[ m_focusIndex ]; - } - if ( layout == Qt::AlignVCenter ) - { - return LUT{ T, I, N, D }[ m_focusIndex ]; - } - if ( layout == ( Qt::AlignLeft | Qt::AlignVCenter ) ) - { - return LUT{ I, N, T, D }[ m_focusIndex ]; - } - if ( layout == ( Qt::AlignRight | Qt::AlignVCenter ) ) - { - return LUT{ I, D, N, T }[ m_focusIndex ]; - } - if ( layout == ( Qt::AlignTop | Qt::AlignHCenter ) ) - { - return LUT{ T, N, D, I }[ m_focusIndex ]; - } - if ( layout == ( Qt::AlignBottom | Qt::AlignHCenter ) ) - { - return LUT{ N, I, D, T }[ m_focusIndex ]; - } - - return None; - } - - FocusIndeces focusIndex() const - { - return m_focusIndex; - } - - void focusNext() - { - const auto index = nextFocusIndex(); - setFocus( index ); - } - - void focusPrevious() - { - const auto index = previousFocusIndex(); - setFocus( index ); - } - - void focusDefault() - { - const auto index = defaultFocusIndex(); - setFocus( index ); - } - - void setFocus( const FocusIndeces index ) - { - using namespace aliased_enum; - Q_ASSERT( index == D || index == T || index == I || index == N ); - if ( index == D || index == T || index == I || index == N ) - { - m_focusIndex = index; - Q_EMIT q->focusIndexChanged( m_focusIndex ); - q->update(); + pressedButton = button; + spinBox->update(); } } - QRectF focusIndicatorRect() const + inline void setHovered( QskSpinBox* spinBox, int button ) { - if ( m_focusIndex == QskSpinBox::Decrement ) + if ( hoveredButton != button ) { - return q->subControlRect( QskSpinBox::DecrementPanel ); + hoveredButton = button; + spinBox->update(); } - if ( m_focusIndex == QskSpinBox::Increment ) - { - return q->subControlRect( QskSpinBox::IncrementPanel ); - } - if ( m_focusIndex == QskSpinBox::Textbox ) - { - return q->subControlRect( QskSpinBox::TextPanel ); - } - return {}; } - void saveMousePosition( const QPointF& pos ) - { - q->setSkinHint( QskSpinBox::Layout | QskAspect::Metric | QskAspect::Position, pos ); - } + int pressedButton = ButtonNone; + int hoveredButton = ButtonNone; - bool focusNow() const - { - const auto focusOnClick = ( q->focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus; - const auto focusOnTouchRelease = QGuiApplication::styleHints()->setFocusOnTouchRelease(); - return focusOnClick && !focusOnTouchRelease; - } - - private: - QskSpinBox* const q; - FocusIndeces m_focusIndex = FocusIndeces::None; + // int decimals; ??? }; -using S = QskSpinBox; - QskSpinBox::QskSpinBox( QQuickItem* const parent ) : Inherited( parent ) - , m_data( std::make_unique< PrivateData >( this ) ) + , m_data( new PrivateData ) { - setBoundaries( 0.0, 1.0 ); + setBoundaries( 0.0, 99.99 ); // this is what QDoubleSpinBox does + setAcceptHoverEvents( true ); setAcceptedMouseButtons( Qt::LeftButton ); setFocusPolicy( Qt::StrongFocus ); - - connect( this, &S::focusIndexChanged, this, &S::focusIndicatorRectChanged ); } -QskSpinBox::~QskSpinBox() = default; - -void QskSpinBox::hoverEnterEvent( QHoverEvent* const event ) +QskSpinBox::~QskSpinBox() { - m_data->saveMousePosition( qskHoverPosition( event ) ); } -void QskSpinBox::hoverLeaveEvent( QHoverEvent* /*const event */ ) +void QskSpinBox::hoverEnterEvent( QHoverEvent* ) { - m_data->saveMousePosition( {} ); } -void QskSpinBox::hoverMoveEvent( QHoverEvent* const event ) +void QskSpinBox::hoverLeaveEvent( QHoverEvent* ) { - m_data->saveMousePosition( qskHoverPosition( event ) ); + m_data->setHovered( this, ButtonNone ); } -void QskSpinBox::mouseReleaseEvent( QMouseEvent* const event ) +void QskSpinBox::hoverMoveEvent( QHoverEvent* event ) { - m_data->saveMousePosition( qskMousePosition( event ) ); + const auto button = buttonAt( this, qskHoverPosition( event ) ); + m_data->setHovered( this, button ); +} - const auto focus = m_data->focusNow(); - - if ( subControlRect( QskSpinBox::IncrementPanel ).contains( event->pos() ) ) +void QskSpinBox::mousePressEvent( QMouseEvent* event ) +{ + if ( const auto button = buttonAt( this, qskMousePosition( event ) ) ) { - increment( +stepSize() ); - - if ( focus ) - { - m_data->setFocus( Increment ); - } + m_data->setPressed( this, button ); + increment( stepSize() * button ); return; } - if ( subControlRect( QskSpinBox::DecrementPanel ).contains( event->pos() ) ) - { - increment( -stepSize() ); - - if ( focus ) - { - m_data->setFocus( Decrement ); - } - - return; - } - - if ( subControlRect( QskSpinBox::TextPanel ).contains( event->pos() ) ) - { - if ( focus ) - { - m_data->setFocus( Textbox ); - } - - return; - } - - event->ignore(); + Inherited::mousePressEvent( event ); } -void QskSpinBox::mousePressEvent( QMouseEvent* const event ) +void QskSpinBox::mouseReleaseEvent( QMouseEvent* ) { - m_data->saveMousePosition( -1 * qskMousePosition( event ) ); - - const auto focus = m_data->focusNow(); - - if ( subControlRect( QskSpinBox::IncrementPanel ).contains( event->pos() ) ) - { - if ( focus ) - { - m_data->setFocus( QskSpinBox::Increment ); - } - return; - } - - if ( subControlRect( QskSpinBox::DecrementPanel ).contains( event->pos() ) ) - { - if ( focus ) - { - m_data->setFocus( QskSpinBox::Decrement ); - } - return; - } - - event->ignore(); + m_data->setPressed( this, ButtonNone ); } void QskSpinBox::keyPressEvent( QKeyEvent* const event ) { switch ( event->key() ) { - case Qt::Key_Plus: - case Qt::Key_Up: - case Qt::Key_Right: - increment( +stepSize() ); - return; case Qt::Key_Minus: case Qt::Key_Down: case Qt::Key_Left: + { increment( -stepSize() ); + m_data->setPressed( this, ButtonDecrement ); return; + } + + case Qt::Key_Plus: + case Qt::Key_Up: + case Qt::Key_Right: + { + increment( +stepSize() ); + m_data->setPressed( this, ButtonIncrement ); + return; + } + +#if 1 case Qt::Key_Select: case Qt::Key_Space: - if ( focusIndex() == Increment ) - { - increment( +stepSize() ); - } - if ( focusIndex() == Decrement ) - { - increment( -stepSize() ); - } + + /* + All keys to navigate along the focus tab chain are not valid + for a spin box, as it accepts number inputs only. Guess this is why + QSpinBox goes straight into edit mode when receiving the focus + + So once setting values by keyboard is implemented we have to decide + how to do it here. TODO ... + */ return; +#endif + default: break; } - const int steps = qskFocusChainIncrement( event ); - - if ( steps < 0 ) - { - for ( int i = 0; i < qAbs( steps ); ++i ) - { - m_data->focusPrevious(); - } - } - - if ( steps > 0 ) - { - for ( int i = 0; i < steps; ++i ) - { - m_data->focusNext(); - } - } - - if ( steps != 0 && m_data->focusIndex() != None ) - { - return; - } - Inherited::keyPressEvent( event ); } void QskSpinBox::keyReleaseEvent( QKeyEvent* const event ) { - if ( event->key() == Qt::Key_Select || event->key() == Qt::Key_Space ) - { - return; - } - + m_data->setPressed( this, ButtonNone ); Inherited::keyReleaseEvent( event ); } -void QskSpinBox::focusInEvent( QFocusEvent* const event ) +int QskSpinBox::pressedButton() const { - if ( event->reason() == Qt::TabFocusReason ) - { - m_data->focusNext(); - return; - } - - if ( event->reason() == Qt::BacktabFocusReason ) - { - m_data->focusPrevious(); - return; - } - - if ( m_data->focusIndex() == QskSpinBox::None ) - { - m_data->focusDefault(); - return; - } - - Inherited::focusInEvent( event ); + return m_data->pressedButton; } -QRectF QskSpinBox::focusIndicatorRect() const +int QskSpinBox::hoveredButton() const { - return m_data->focusIndicatorRect(); -} - -QskSpinBox::FocusIndeces QskSpinBox::focusIndex() const -{ - return m_data->focusIndex(); + return m_data->hoveredButton; } diff --git a/src/controls/QskSpinBox.h b/src/controls/QskSpinBox.h index 3cd17a6b..39f7f99c 100644 --- a/src/controls/QskSpinBox.h +++ b/src/controls/QskSpinBox.h @@ -8,68 +8,30 @@ #include - -//////////////////////////////////////////////////////////////////////////////////////////////////// -/// @brief -/// This control allows the user to increment and decrement a floating point value. -/// @details -/// The incement and decrement step size is configurable and the value's range can be limited -/// through an inclusive interval [min,max]. The value is being displayed on a readonly text label -/// surrounded by an increment and decrement button. -/// -/// - The value can be increased by: -/// - clicking the increment button -/// - pressing the plus, right or up key -/// - scrolling up the mouse wheel -/// - focusing the increment button and clicking space or select -/// - The value can be decreased by: -/// - clicking the decrement button -/// - pressing the minus, left or down key -/// - scrolling down the mouse wheel -/// - focusing the decrement button and clicking space or select -//////////////////////////////////////////////////////////////////////////////////////////////////// class QSK_EXPORT QskSpinBox : public QskBoundedValueInput { Q_OBJECT using Inherited = QskBoundedValueInput; public: - /// Focus indeces for the visual subcontrols - enum FocusIndeces : int - { - Decrement = 0, ///< the decrement buttons index - Textbox = 1, ///< the textbox' index - Increment = 2, ///< the increment button's index - None = 3 ///< index for when no subcontrol is focused (e.g. focus in/out ) - }; - Q_ENUM( FocusIndeces ) - /// The currently focused subcontrol's index - Q_PROPERTY( FocusIndeces focusIndex READ focusIndex NOTIFY focusIndexChanged ) - QSK_SUBCONTROLS( IncrementPanel ) ///< Use this to style the increment button. - QSK_SUBCONTROLS( DecrementPanel ) ///< Use this to style the decrement button. - QSK_SUBCONTROLS( IncrementText ) ///< Use this to style the increment button's text. - QSK_SUBCONTROLS( DecrementText ) ///< Use this to style the decrement button's text. - QSK_SUBCONTROLS( TextPanel ) ///< Use this to style the text's panel. - QSK_SUBCONTROLS( Text ) ///< Use this to style the text (e.g. font role). - QSK_SUBCONTROLS( Layout ) ///< Use this to style the spinbox's controls layout. + QSK_SUBCONTROLS( Panel, TextPanel, Text, + IncrementPanel, IncrementIndicator, DecrementPanel, DecrementIndicator ) + QSK_STATES( Pressed ) - /// @brief C-TOR - /// @param parent This object's parent - explicit QskSpinBox( QQuickItem* parent = nullptr ); - - /// @brief D-TOR defaulted but required for std::unique_ptr + QskSpinBox( QQuickItem* parent = nullptr ); ~QskSpinBox() override; - /// @brief Getter for property focusIndex. - /// @returns Returns the currently focused subcontrol's index. - /// @retval Return FocusIndeces::None if no subcontrol is currently focused. - FocusIndeces focusIndex() const; - - Q_SIGNALS: - /// @brief Emitted when the property @c focusIndex changed. - void focusIndexChanged( int index ); +#if 1 + /* + -1: decrease + 0: none + 1: increment + */ + int pressedButton() const; + int hoveredButton() const; +#endif private: void hoverEnterEvent( QHoverEvent* event ) override; @@ -82,9 +44,6 @@ class QSK_EXPORT QskSpinBox : public QskBoundedValueInput void keyPressEvent( QKeyEvent* event ) override; void keyReleaseEvent( QKeyEvent* event ) override; - void focusInEvent( QFocusEvent* event ) override; - QRectF focusIndicatorRect() const override; - class PrivateData; std::unique_ptr< PrivateData > m_data; }; diff --git a/src/controls/QskSpinBoxSkinlet.cpp b/src/controls/QskSpinBoxSkinlet.cpp index 45973a46..5f7cd61b 100644 --- a/src/controls/QskSpinBoxSkinlet.cpp +++ b/src/controls/QskSpinBoxSkinlet.cpp @@ -5,18 +5,18 @@ #include "QskSpinBoxSkinlet.h" #include "QskSpinBox.h" -#include + #include namespace { inline QPointF cursorPosSkinHint( const QskSpinBox& spinbox ) { - const auto aspect = QskSpinBox::Layout | QskAspect::Metric | QskAspect::Position; + const auto aspect = QskSpinBox::Panel | QskAspect::Metric | QskAspect::Position; return spinbox.effectiveSkinHint( aspect ).toPointF(); } - enum SampleIndeces + enum { Dec = 0, Txt = 1, @@ -27,16 +27,17 @@ namespace QskSpinBoxSkinlet::QskSpinBoxSkinlet( QskSkin* ) { - setNodeRoles( - { IncrementPanel, IncrementText, DecrementPanel, DecrementText, TextPanel, TextText } ); + setNodeRoles( { IncrementPanel, DecrementPanel, TextPanel, + IncrementIndicator, DecrementIndicator, Text } ); } -int QskSpinBoxSkinlet::sampleCount( const QskSkinnable*, QskAspect::Subcontrol ) const +int QskSpinBoxSkinlet::sampleCount( + const QskSkinnable*, QskAspect::Subcontrol ) const { return 1; } -QRectF QskSpinBoxSkinlet::sampleRect( const QskSkinnable* const skinnable, const QRectF& rect, +QRectF QskSpinBoxSkinlet::sampleRect( const QskSkinnable* skinnable, const QRectF& rect, QskAspect::Subcontrol subControl, int index ) const { if ( index == Dec || index == Inc || index == Txt ) @@ -51,6 +52,7 @@ QskAspect::States QskSpinBoxSkinlet::sampleStates( const QskSkinnable* const skinnable, QskAspect::Subcontrol subControl, int index ) const { using S = QskSpinBox; + auto states = Inherited::sampleStates( skinnable, subControl, index ); if ( subControl == S::DecrementPanel || subControl == S::IncrementPanel || @@ -59,98 +61,100 @@ QskAspect::States QskSpinBoxSkinlet::sampleStates( const auto* const spinbox = static_cast< const S* >( skinnable ); const auto cursorPos = cursorPosSkinHint( *spinbox ); const QPointF cursorPosAbs{ qAbs( cursorPos.x() ), qAbs( cursorPos.y() ) }; - const auto focusIndex = spinbox->focusIndex(); const auto subControlRect = spinbox->subControlRect( subControl ); const auto contain = !cursorPosAbs.isNull() && subControlRect.contains( cursorPosAbs ); const auto pressed = contain && ( cursorPos.x() < 0 || cursorPos.y() < 0 ); const auto hovered = contain && !pressed; - const auto focused = ( subControl == S::IncrementPanel && focusIndex == S::Increment ) || - ( subControl == S::DecrementPanel && focusIndex == S::Decrement ) || - ( subControl == S::TextPanel && focusIndex == S::Textbox ); states.setFlag( QskControl::Hovered, hovered ); states.setFlag( QskSpinBox::Pressed, pressed ); - states.setFlag( QskControl::Focused, focused ); } return states; } -QSizeF QskSpinBoxSkinlet::sizeHint( - const QskSkinnable* const skinnable, Qt::SizeHint sizeHint, const QSizeF& size ) const +QSizeF QskSpinBoxSkinlet::sizeHint( const QskSkinnable* skinnable, + Qt::SizeHint which, const QSizeF& size ) const { + if ( which != Qt::PreferredSize ) + return QSizeF(); + using S = QskSpinBox; - const auto* const spinbox = static_cast< const S* >( skinnable ); - const auto layout = spinbox->alignmentHint( S::Layout ); - const auto spacing = spinbox->spacingHint( S::Layout ); + + const auto spinbox = static_cast< const QskSpinBox* >( skinnable ); + + const auto layout = spinbox->alignmentHint( S::Panel ); + const auto spacing = spinbox->spacingHint( S::Panel ); const auto strutInc = spinbox->strutSizeHint( S::IncrementPanel ); const auto strutDec = spinbox->strutSizeHint( S::DecrementPanel ); const auto strutTxt = spinbox->strutSizeHint( S::TextPanel ); - if ( sizeHint == Qt::MinimumSize || sizeHint == Qt::MaximumSize || Qt::PreferredSize ) + if ( layout == Qt::AlignTop || layout == Qt::AlignBottom || layout == Qt::AlignVCenter ) { - if ( layout == Qt::AlignTop || layout == Qt::AlignBottom || layout == Qt::AlignVCenter ) - { - const auto w = qMax( strutDec.width(), qMax( strutTxt.width(), strutInc.width() ) ); - const auto h = strutDec.height() + strutTxt.height() + strutInc.height(); - return { w, h + 2.0 * spacing }; - } - if ( layout == Qt::AlignLeft || layout == Qt::AlignRight || layout == Qt::AlignHCenter ) - { - const auto w = strutDec.width() + strutTxt.width() + strutInc.width(); - const auto h = qMax( strutDec.height(), qMax( strutTxt.height(), strutInc.height() ) ); - return { w + 2.0 * spacing, h }; - } - if ( layout == ( Qt::AlignLeft | Qt::AlignVCenter ) || - layout == ( Qt::AlignRight | Qt::AlignVCenter ) ) - { - const auto w = strutTxt.width() + qMax( strutInc.width(), strutDec.width() ); - const auto h = - qMax( 2.0 * qMax( strutInc.height(), strutDec.height() ), strutTxt.height() ); - return { w + spacing, h + spacing }; - } - if ( layout == ( Qt::AlignTop | Qt::AlignHCenter ) || - layout == ( Qt::AlignTop | Qt::AlignHCenter ) ) - { - const auto w = qMax( strutTxt.width(), strutInc.width() + strutDec.width() ); - const auto h = strutTxt.height() + qMax( strutInc.height(), strutDec.height() ); - return { w + spacing, h + spacing }; - } + const auto w = qMax( strutDec.width(), qMax( strutTxt.width(), strutInc.width() ) ); + const auto h = strutDec.height() + strutTxt.height() + strutInc.height(); + + return QSizeF( w, h + 2.0 * spacing ); } - return Inherited::sizeHint( skinnable, sizeHint, size ); + + if ( layout == Qt::AlignLeft || layout == Qt::AlignRight || layout == Qt::AlignHCenter ) + { + const auto w = strutDec.width() + strutTxt.width() + strutInc.width(); + const auto h = qMax( strutDec.height(), qMax( strutTxt.height(), strutInc.height() ) ); + + return QSizeF( w + 2.0 * spacing, h ); + } + + if ( layout == ( Qt::AlignLeft | Qt::AlignVCenter ) || + layout == ( Qt::AlignRight | Qt::AlignVCenter ) ) + { + const auto w = strutTxt.width() + qMax( strutInc.width(), strutDec.width() ); + const auto h = + qMax( 2.0 * qMax( strutInc.height(), strutDec.height() ), strutTxt.height() ); + + return QSizeF( w + spacing, h + spacing ); + } + + if ( layout == ( Qt::AlignTop | Qt::AlignHCenter ) || + layout == ( Qt::AlignTop | Qt::AlignHCenter ) ) + { + const auto w = qMax( strutTxt.width(), strutInc.width() + strutDec.width() ); + const auto h = strutTxt.height() + qMax( strutInc.height(), strutDec.height() ); + + return QSizeF( w + spacing, h + spacing ); + } + + return Inherited::sizeHint( skinnable, which, size ); } -QRectF QskSpinBoxSkinlet::subControlRect( const QskSkinnable* const skinnable, const QRectF& rect, - QskAspect::Subcontrol subControl ) const +QRectF QskSpinBoxSkinlet::subControlRect( const QskSkinnable* skinnable, + const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const { - using S = QskSpinBox; + using Q = QskSpinBox; - if ( subControl == S::DecrementText ) - { - return subControlRect( skinnable, rect, S::DecrementPanel ); - } - if ( subControl == S::IncrementText ) - { - return subControlRect( skinnable, rect, S::IncrementPanel ); - } - if ( subControl == S::Text ) - { - return subControlRect( skinnable, rect, S::TextPanel ); - } + if ( subControl == Q::DecrementIndicator ) + return skinnable->subControlContentsRect( contentsRect, Q::DecrementPanel ); - const auto* const spinbox = static_cast< const S* >( skinnable ); - const auto layout = spinbox->alignmentHint( S::Layout ); - const auto spacing = spinbox->spacingHint( S::Layout ); + if ( subControl == Q::IncrementIndicator ) + return skinnable->subControlContentsRect( contentsRect, Q::IncrementPanel ); + + if ( subControl == Q::Text ) + return skinnable->subControlContentsRect( contentsRect, Q::TextPanel ); + + const auto* const spinbox = static_cast< const QskSpinBox* >( skinnable ); + + const auto layout = spinbox->alignmentHint( Q::Panel ); + const auto spacing = spinbox->spacingHint( Q::Panel ); std::array< QRectF, Count > rects = { - QRectF{ QPointF{}, spinbox->strutSizeHint( S::DecrementPanel ) }, - QRectF{ QPointF{}, spinbox->strutSizeHint( S::TextPanel ) }, - QRectF{ QPointF{}, spinbox->strutSizeHint( S::IncrementPanel ) }, + QRectF{ QPointF(), spinbox->strutSizeHint( Q::DecrementPanel ) }, + QRectF{ QPointF(), spinbox->strutSizeHint( Q::TextPanel ) }, + QRectF{ QPointF(), spinbox->strutSizeHint( Q::IncrementPanel ) }, }; - const auto center = rect.center(); + const auto center = contentsRect.center(); if ( layout == Qt::AlignLeft ) { @@ -234,62 +238,66 @@ QRectF QskSpinBoxSkinlet::subControlRect( const QskSkinnable* const skinnable, c { center.x() + spacing * 0.5, rects[ Txt ].top() - spacing - rects[ Inc ].height() } ); } - if ( subControl == S::DecrementPanel ) - { + if ( subControl == Q::DecrementPanel ) return rects[ Dec ]; - } - if ( subControl == S::TextPanel ) - { - return rects[ Txt ]; - } - if ( subControl == S::IncrementPanel ) - { - return rects[ Inc ]; - } - return Inherited::subControlRect( skinnable, rect, subControl ); + if ( subControl == Q::TextPanel ) + return rects[ Txt ]; + + if ( subControl == Q::IncrementPanel ) + return rects[ Inc ]; + + return Inherited::subControlRect( skinnable, contentsRect, subControl ); } QSGNode* QskSpinBoxSkinlet::updateSubNode( - const QskSkinnable* const skinnable, const quint8 nodeRole, QSGNode* const node ) const + const QskSkinnable* skinnable, const quint8 nodeRole, QSGNode* const node ) const { - using S = QskSpinBox; - if ( nodeRole == IncrementPanel ) + using Q = QskSpinBox; + + switch( nodeRole ) { - return updateSeriesNode( skinnable, S::IncrementPanel, node ); - } - if ( nodeRole == DecrementPanel ) - { - return updateSeriesNode( skinnable, S::DecrementPanel, node ); - } - if ( nodeRole == IncrementText ) - { - return updateTextNode( skinnable, node, QStringLiteral( "+" ), S::IncrementText ); - } - if ( nodeRole == DecrementText ) - { - return updateTextNode( skinnable, node, QStringLiteral( "-" ), S::DecrementText ); - } - if ( nodeRole == TextPanel ) - { - return updateSeriesNode( skinnable, S::TextPanel, node ); - } - if ( nodeRole == TextText ) - { - const auto* const spinbox = static_cast< const S* >( skinnable ); - return updateTextNode( skinnable, node, QString::number( spinbox->value() ), S::Text ); + case IncrementPanel: + return updateSeriesNode( skinnable, Q::IncrementPanel, node ); + + case DecrementPanel: + return updateSeriesNode( skinnable, Q::DecrementPanel, node ); + + case IncrementIndicator: + { + return updateTextNode( skinnable, node, + QStringLiteral( "+" ), Q::IncrementIndicator ); + } + + case DecrementIndicator: + { + return updateTextNode( skinnable, node, + QStringLiteral( "-" ), Q::DecrementIndicator ); + } + + case TextPanel: + return updateSeriesNode( skinnable, Q::TextPanel, node ); + + case Text: + { + const auto* const spinbox = static_cast< const QskSpinBox* >( skinnable ); + + return updateTextNode( skinnable, node, + QString::number( spinbox->value() ), Q::Text ); + } } + return Inherited::updateSubNode( skinnable, nodeRole, node ); } -QSGNode* QskSpinBoxSkinlet::updateSampleNode( const QskSkinnable* const skinnable, +QSGNode* QskSpinBoxSkinlet::updateSampleNode( const QskSkinnable* skinnable, QskAspect::Subcontrol subControl, const int index, QSGNode* const node ) const { - using S = QskSpinBox; - const auto* const spinbox = static_cast< const S* >( skinnable ); + using Q = QskSpinBox; + const auto* const spinbox = static_cast< const QskSpinBox* >( skinnable ); - if ( subControl == S::DecrementPanel || subControl == S::IncrementPanel || - subControl == S::TextPanel ) + if ( subControl == Q::DecrementPanel || subControl == Q::IncrementPanel || + subControl == Q::TextPanel ) { const auto rect = sampleRect( spinbox, spinbox->contentsRect(), subControl, index ); return updateBoxNode( skinnable, node, rect, subControl ); diff --git a/src/controls/QskSpinBoxSkinlet.h b/src/controls/QskSpinBoxSkinlet.h index 6697c5de..a6e1a03e 100644 --- a/src/controls/QskSpinBoxSkinlet.h +++ b/src/controls/QskSpinBoxSkinlet.h @@ -8,106 +8,47 @@ #include -//////////////////////////////////////////////////////////////////////////////////////////////////// -/// @brief -/// This skinlet's purpose is to draw a QskSpinBox instance. -/// @details -/// In order to manage individual subcontrol states this skinlet uses subcontrol sampling. Although -/// it is most usefull when dealing with dynamic or large numbers of subcontrols, it is a strategy -/// to index the subcontrol in order to have individual states instead of one collective state on -/// the skinnable object. -/// @note The placement and dimensions of all subcontrols depend on the following subctrontrol -/// aspects: -/// - QskSpinBox::Layout's alignment hint ( which affects the positions of all controls ) -/// - QskSpinBox::Layout's spacing hint -/// - QskSpinBox::IncrementPanel's strut size hint -/// - QskSpinBox::DecrementPanel's strut size hint -/// - QskSpinBox::TextPanel's strut size hint -//////////////////////////////////////////////////////////////////////////////////////////////////// class QSK_EXPORT QskSpinBoxSkinlet : public QskSkinlet { Q_GADGET using Inherited = QskSkinlet; public: - /// @brief C-TOR defining the correct node's role order (e.g. panel before text) Q_INVOKABLE QskSpinBoxSkinlet( QskSkin* = nullptr ); - /// @brief Roles for the subcontrols. enum NodeRole { - IncrementPanel, ///< Identifier for the increment button's panel. - IncrementText, ///< Identifier for the increment button's text. - DecrementPanel, ///< Identifier for the decrement button's panel. - DecrementText, ///< Identifier for the decrement button's text. - TextPanel, ///< Identifier for the text's panel. - TextText, ///< Identifier for the text's glyphs. - RoleCount ///< Number of all roles in this skinlet. + IncrementPanel, + DecrementPanel, + TextPanel, + + IncrementIndicator, + DecrementIndicator, + Text, + + RoleCount }; - protected: - /// @brief Getter for the number of samples in this skinlet. - /// @param skinnable The skinnable object. - /// @param subControl The skinnable object's subcontrol. - /// @returns Returns the number of samples. - /// @retval Returns 1 since each subcontrol a sample; + QRectF subControlRect( const QskSkinnable*, + const QRectF&, QskAspect::Subcontrol ) const override; + + QSizeF sizeHint( const QskSkinnable* skinnable, + Qt::SizeHint, const QSizeF& ) const override; + int sampleCount( const QskSkinnable* skinnable, QskAspect::Subcontrol subControl ) const override; - /// @brief Getter for a subcontrol's sample rectangle. - /// @param skinnable The skinnable object. - /// @param rect The skinnable object's content rectangle. - /// @param subControl The skinnable object's subcontrol. - /// @param index The skinnable object's subcontrol sample index. - /// @returns Returns the subcontrol's rectangle within the @p skinnable's content rectangle. QRectF sampleRect( const QskSkinnable* skinnable, const QRectF& rect, QskAspect::Subcontrol subControl, int index ) const override; - /// @brief Getter for a subcontrol's sample states. - /// @param skinnable The skinnable object. - /// @param subControl The skinnable object's subcontrol. - /// @param index The skinnable object's subcontrol sample index. - /// @return Returns the states of the subcontrol's sample at the given @p index. - /// @details Sets or unsets the @c pressed, @c hovered, @c focused bits in the returned states - /// object. - QskAspect::States sampleStates( - const QskSkinnable* skinnable, QskAspect::Subcontrol subControl, int index ) const override; + QskAspect::States sampleStates( const QskSkinnable*, + QskAspect::Subcontrol, int index ) const override; - /// @brief Getter for the skinnable object's size hints. - /// @param skinnable The skinnable object. - /// @param sizeHint The size hint. - /// @param rect The skinnable object's available rectangle. - /// @details Calculates the minimum, maximum and preferred size of the skinnable. - QSizeF sizeHint( - const QskSkinnable* skinnable, Qt::SizeHint sizeHint, const QSizeF& rect ) const override; - - /// @brief Getter for the subcontrol's rectangle. - /// @param skinnable The skinnable object. - /// @param rect The skinnable object's content rectangle. - /// @param subControl The skinnable object's subcontrol. - /// @returns Returns the subcontrol's rectangle in the skinnable's content rectangle. - QRectF subControlRect( const QskSkinnable* skinnable, const QRectF& rect, - QskAspect::Subcontrol subControl ) const override; - - /// @brief Updates the scene graph @p node for the given @p role - /// @param skinnable The skinnable object. - /// @param role The node's role number ( see: QskSpinBoxSkinlet::NodeRole ). - /// @param node The scene graph node for the given @p role. - /// @returns Returns a new or updated scene graph node for the given @p role. - /// @details This functions updates the text nodes and mediates updates for sampled - /// subcontrols to QskSpinBoxSkinlet::updateSampleNode. - /// @see QskSpinBoxSkinlet::NodeRole + protected: QSGNode* updateSubNode( const QskSkinnable* skinnable, quint8 role, QSGNode* node ) const override; - /// @brief Updates the scene graph @p node for the given @p subControl's sample @p index - /// @param skinnable The skinnable object. - /// @param subControl The skinnable object's subcontrol. - /// @param index The skinnable object's subcontrol sample index. - /// @param node The scene graph node for the @p subControl's sample @p index. - /// @returns Returns a new or updated scene graph node for the given @p subControl's sample @p - /// index. - QSGNode* updateSampleNode( const QskSkinnable* skinnable, QskAspect::Subcontrol subControl, + QSGNode* updateSampleNode( const QskSkinnable*, QskAspect::Subcontrol, int index, QSGNode* node ) const override; };