focus handling removed ( spin boxes do not have an internal focus chain

), formal adjustments
This commit is contained in:
Uwe Rathmann 2023-02-19 14:24:09 +01:00
parent 26661dcbb6
commit 0c2bc32abd
8 changed files with 430 additions and 713 deletions

View File

@ -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.
*/

View File

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

View File

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

View File

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

View File

@ -7,454 +7,171 @@
#include "QskIntervalF.h"
#include "QskEvent.h"
#include <qguiapplication.h>
#include <qstylehints.h>
#include <qmath.h>
QSK_SUBCONTROL( QskSpinBox, Panel )
#include <array>
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;
}

View File

@ -8,68 +8,30 @@
#include <QskBoundedValueInput.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @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;
};

View File

@ -5,18 +5,18 @@
#include "QskSpinBoxSkinlet.h"
#include "QskSpinBox.h"
#include <QFontMetrics>
#include <array>
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 );

View File

@ -8,106 +8,47 @@
#include <QskSkinlet.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @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;
};