From e7b64bf4a0116c61ff758b71779576972e5e5e3f Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Tue, 30 Apr 2024 13:03:59 +0200 Subject: [PATCH 1/4] QskSlider: Adapt to new Material3 style Resolves #391 --- designsystems/material3/QskMaterial3Skin.cpp | 57 +++++++++----------- designsystems/material3/QskMaterial3Skin.h | 5 ++ src/controls/QskSliderSkinlet.cpp | 34 +++++++++--- 3 files changed, 57 insertions(+), 39 deletions(-) diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index b4664475..82ff1ddb 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -839,41 +839,34 @@ void Editor::setupSlider() using A = QskAspect; using Q = QskSlider; - const qreal extent = 30_dp; + const QSizeF sliderSize( 48_dp, 44_dp ); + setStrutSize( Q::Panel | A::Horizontal, sliderSize ); + setStrutSize( Q::Panel | A::Vertical, sliderSize.transposed() ); - // Panel - - setMetric( Q::Panel | A::Size, extent ); - setBoxShape( Q::Panel, 0 ); - setBoxBorderMetrics( Q::Panel, 0 ); - setGradient( Q::Panel, QskGradient() ); - - setPadding( Q::Panel | A::Horizontal, QskMargins( 0.5 * extent, 0 ) ); - setPadding( Q::Panel | A::Vertical, QskMargins( 0, 0.5 * extent ) ); - - // Groove, Fill - - for ( auto subControl : { Q::Groove, Q::Fill } ) - { - setPadding( subControl, 0 ); - - setBoxShape( subControl, 0 ); - setBoxBorderMetrics( subControl, 0 ); - } - - setMetric( Q::Groove | A::Size, 4_dp ); - setMetric( Q::Fill | A::Size, 6_dp ); + setBoxShape( Q::Groove | A::Horizontal, { 0, 100, 0, 100, Qt::RelativeSize } ); + setBoxShape( Q::Groove | A::Vertical, { 100, 100, 0, 0, Qt::RelativeSize } ); + setMetric( Q::Groove | A::Size, 16_dp ); + setMargin( Q::Groove | A::Horizontal, { 6_dp, 0, 0, 0 } ); + setMargin( Q::Groove | A::Vertical, {0, 0, 0, 6_dp } ); setGradient( Q::Groove, m_pal.primaryContainer ); setGradient( Q::Groove | Q::Disabled, m_pal.onSurface12 ); + setBoxShape( Q::Fill | A::Horizontal, { 100, 0, 100, 0, Qt::RelativeSize } ); + setBoxShape( Q::Fill | A::Vertical, { 0, 0, 100, 100, Qt::RelativeSize } ); + setMetric( Q::Fill | A::Size, 16_dp ); + setMargin( Q::Fill | A::Horizontal, { 0, 0, 6_dp, 0 } ); + setMargin( Q::Fill | A::Vertical, {0, 6_dp, 0, 0 } ); + setGradient( Q::Fill, m_pal.primary ); setGradient( Q::Fill | Q::Disabled, m_pal.onSurface38 ); setBoxShape( Q::Handle, 100, Qt::RelativeSize ); setBoxBorderMetrics( Q::Handle, 0 ); - setStrutSize( Q::Handle, 20_dp, 20_dp ); + const QSizeF handleSize( 4_dp, 44_dp ); + setStrutSize( Q::Handle | A::Horizontal, handleSize ); + setStrutSize( Q::Handle | A::Vertical, handleSize.transposed() ); setGradient( Q::Handle, m_pal.primary ); setGradient( Q::Handle | Q::Pressed, m_pal.primary ); @@ -881,13 +874,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 ); - setBoxShape( Q::Ripple, 100, Qt::RelativeSize ); - setGradient( Q::Ripple, Qt::transparent ); - setGradient( Q::Ripple | Q::Hovered, m_pal.primary12 ); - setGradient( Q::Ripple | Q::Pressed, m_pal.primary12 ); - - // move the handle smoothly, when using keys + // move the handle smoothly when using keys setAnimation( Q::Handle | A::Metric | A::Position, 2 * qskDuration ); setAnimation( Q::Handle | A::Metric | A::Position | Q::Pressed, 0 ); } @@ -1309,6 +1296,7 @@ QskMaterial3Theme::QskMaterial3Theme( QskSkin::ColorScheme colorScheme, onPrimary = color.toned( 100 ).rgb(); primaryContainer = color.toned( 90 ).rgb(); onPrimaryContainer = color.toned( 10 ).rgb(); + inversePrimary = color.toned( 80 ).rgb(); } { @@ -1345,6 +1333,9 @@ QskMaterial3Theme::QskMaterial3Theme( QskSkin::ColorScheme colorScheme, onBackground = color.toned( 10 ).rgb(); surface = color.toned( 99 ).rgb(); onSurface = color.toned( 10 ).rgb(); + inverseSurface = color.toned( 20 ).rgb(); + inverseOnSurface = color.toned( 95 ).rgb(); + scrim = color.toned( 0 ).rgb(); shadow = color.toned( 0 ).rgb(); } @@ -1368,6 +1359,7 @@ QskMaterial3Theme::QskMaterial3Theme( QskSkin::ColorScheme colorScheme, onPrimary = color.toned( 20 ).rgb(); primaryContainer = color.toned( 30 ).rgb(); onPrimaryContainer = color.toned( 90 ).rgb(); + inversePrimary = color.toned( 40 ).rgb(); } { @@ -1404,6 +1396,9 @@ QskMaterial3Theme::QskMaterial3Theme( QskSkin::ColorScheme colorScheme, onBackground = color.toned( 90 ).rgb(); surface = color.toned( 10 ).rgb(); onSurface = color.toned( 80 ).rgb(); + inverseSurface = color.toned( 90 ).rgb(); + inverseOnSurface = color.toned( 20 ).rgb(); + scrim = color.toned( 0 ).rgb(); shadow = color.toned( 0 ).rgb(); } diff --git a/designsystems/material3/QskMaterial3Skin.h b/designsystems/material3/QskMaterial3Skin.h index b790addb..06d8fd22 100644 --- a/designsystems/material3/QskMaterial3Skin.h +++ b/designsystems/material3/QskMaterial3Skin.h @@ -81,6 +81,11 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Theme QRgb surfaceContainerHighest; + QRgb inverseSurface; + QRgb inverseOnSurface; + QRgb inversePrimary; + + QRgb scrim; QRgb shadow; QskShadowMetrics elevation0; diff --git a/src/controls/QskSliderSkinlet.cpp b/src/controls/QskSliderSkinlet.cpp index 9dbc720b..1b6810d6 100644 --- a/src/controls/QskSliderSkinlet.cpp +++ b/src/controls/QskSliderSkinlet.cpp @@ -161,9 +161,24 @@ QRectF QskSliderSkinlet::innerRect( const QskSlider* slider, } QRectF QskSliderSkinlet::grooveRect( - const QskSlider* slider, const QRectF& rect ) const + const QskSlider* slider, const QRectF& contentsRect ) const { - return innerRect( slider, rect, QskSlider::Groove ); + const auto r = qskInnerPanelRect( slider, contentsRect ); + auto grooveRect = innerRect( slider, contentsRect, QskSlider::Groove ); + const auto pos = qBound( 0.0, slider->handlePosition(), 1.0 ); + + if ( slider->orientation() == Qt::Horizontal ) + { + grooveRect.setLeft( r.left() + pos * r.width() ); + grooveRect.setRight( r.right() ); + } + else + { + grooveRect.setBottom( r.bottom() - pos * r.height() ); + grooveRect.setTop( r.top() ); + } + + return grooveRect; } QRectF QskSliderSkinlet::scaleRect( @@ -254,14 +269,17 @@ QSizeF QskSliderSkinlet::sizeHint( const QskSkinnable* skinnable, if ( which != Qt::PreferredSize ) return QSizeF(); - const auto slider = static_cast< const QskSlider* >( skinnable ); + const auto panelHint = skinnable->strutSizeHint( QskSlider::Panel ); + const auto grooveHint = skinnable->strutSizeHint( QskSlider::Groove ); + const auto fillHint = skinnable->strutSizeHint( QskSlider::Fill ); + const auto handleHint = skinnable->strutSizeHint( QskSlider::Handle ); - // strutSizeHint( ... ) ??? - const qreal m1 = slider->metric( QskSlider::Panel | QskAspect::Size ); - const qreal m2 = 4 * m1; + auto hint = panelHint; + hint = hint.expandedTo( grooveHint ); + hint = hint.expandedTo( fillHint ); + hint = hint.expandedTo( handleHint ); - return ( slider->orientation() == Qt::Horizontal ) - ? QSizeF( m2, m1 ) : QSizeF( m1, m2 ); + return hint; } #include "moc_QskSliderSkinlet.cpp" From 3fdd72c5a3c1493f2b5ca9aa2c67b622650a41e2 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Mon, 13 May 2024 16:11:03 +0200 Subject: [PATCH 2/4] QskSlider: Add label container and text ... as required by M3 Resolves #391 --- designsystems/material3/QskMaterial3Skin.cpp | 17 ++++- examples/gallery/inputs/InputPage.cpp | 2 + src/controls/QskSlider.cpp | 2 + src/controls/QskSlider.h | 2 +- src/controls/QskSliderSkinlet.cpp | 69 +++++++++++++++++++- src/controls/QskSliderSkinlet.h | 5 ++ 6 files changed, 94 insertions(+), 3 deletions(-) diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index 82ff1ddb..a7b7c6c4 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -874,6 +874,21 @@ void Editor::setupSlider() const auto disabledColor = flattenedColor( m_pal.onSurface, m_pal.background, 0.38 ); setGradient( Q::Handle | Q::Disabled, disabledColor ); + for( const auto state : { Q::Focused, Q::Pressed } ) + { + setStrutSize( Q::LabelContainer | state, 48_dp, 44_dp, + { QskStateCombination::CombinationNoState, Q::Hovered } ); + } + + setBoxShape( Q::LabelContainer, 100, Qt::RelativeSize ); + setGradient( Q::LabelContainer, m_pal.inverseSurface ); + setMargin( Q::LabelContainer | A::Horizontal, { 0, 0, 0, 4_dp } ); + setMargin( Q::LabelContainer | A::Vertical, { 4_dp, 0, 0, 0 } ); + + setFontRole( Q::LabelText, LabelMedium ); + setColor( Q::LabelText, m_pal.inverseOnSurface ); + setAlignment( Q::LabelText, Qt::AlignCenter ); + // move the handle smoothly when using keys setAnimation( Q::Handle | A::Metric | A::Position, 2 * qskDuration ); setAnimation( Q::Handle | A::Metric | A::Position | Q::Pressed, 0 ); @@ -1471,7 +1486,7 @@ static inline QFont createFont( int size, int lineHeight, } checkFont = false; } - + font.setPixelSize( pixelSize ); if ( spacing > 0.0 ) diff --git a/examples/gallery/inputs/InputPage.cpp b/examples/gallery/inputs/InputPage.cpp index 8f41c912..e5be225b 100644 --- a/examples/gallery/inputs/InputPage.cpp +++ b/examples/gallery/inputs/InputPage.cpp @@ -79,6 +79,8 @@ InputPage::InputPage( QQuickItem* parent ) auto inputBox = new InputBox(); auto gridBox = new QskGridBox( this ); + gridBox->setSpacing( 30 ); + gridBox->setMargins( 30 ); gridBox->addItem( sliderV, 0, 0, -1, 1 ); gridBox->addItem( sliderH, 0, 1, 1, -1 ); diff --git a/src/controls/QskSlider.cpp b/src/controls/QskSlider.cpp index 1c3822fb..fd865c03 100644 --- a/src/controls/QskSlider.cpp +++ b/src/controls/QskSlider.cpp @@ -15,6 +15,8 @@ QSK_SUBCONTROL( QskSlider, Fill ) QSK_SUBCONTROL( QskSlider, Scale ) QSK_SUBCONTROL( QskSlider, Handle ) QSK_SUBCONTROL( QskSlider, Ripple ) +QSK_SUBCONTROL( QskSlider, LabelContainer ) +QSK_SUBCONTROL( QskSlider, LabelText ) QSK_SYSTEM_STATE( QskSlider, Pressed, QskAspect::FirstSystemState << 2 ) diff --git a/src/controls/QskSlider.h b/src/controls/QskSlider.h index 908e96f8..eb50c260 100644 --- a/src/controls/QskSlider.h +++ b/src/controls/QskSlider.h @@ -25,7 +25,7 @@ class QSK_EXPORT QskSlider : public QskBoundedValueInput using Inherited = QskBoundedValueInput; public: - QSK_SUBCONTROLS( Panel, Groove, Fill, Scale, Handle, Ripple ) + QSK_SUBCONTROLS( Panel, Groove, Fill, Scale, Handle, Ripple, LabelContainer, LabelText ) QSK_STATES( Pressed ) explicit QskSlider( QQuickItem* parent = nullptr ); diff --git a/src/controls/QskSliderSkinlet.cpp b/src/controls/QskSliderSkinlet.cpp index 1b6810d6..7a1a161c 100644 --- a/src/controls/QskSliderSkinlet.cpp +++ b/src/controls/QskSliderSkinlet.cpp @@ -10,6 +10,8 @@ #include "QskBoxBorderMetrics.h" #include "QskFunctions.h" +#include + static inline QRectF qskInnerPanelRect( const QskSlider* slider, const QRectF& contentsRect ) { @@ -31,7 +33,7 @@ static inline QRectF qskInnerPanelRect( QskSliderSkinlet::QskSliderSkinlet( QskSkin* skin ) : Inherited( skin ) { - setNodeRoles( { PanelRole, GrooveRole, FillRole, HandleRole, RippleRole } ); + setNodeRoles( { PanelRole, GrooveRole, FillRole, HandleRole, RippleRole, LabelContainerRole, LabelTextRole } ); } QskSliderSkinlet::~QskSliderSkinlet() @@ -73,6 +75,16 @@ QRectF QskSliderSkinlet::subControlRect( const QskSkinnable* skinnable, return rippleRect( slider, contentsRect ); } + if ( subControl == QskSlider::LabelContainer ) + { + return labelContainerRect( slider, contentsRect ); + } + + if ( subControl == QskSlider::LabelText ) + { + return labelContainerRect( slider, contentsRect ); + } + return Inherited::subControlRect( skinnable, contentsRect, subControl ); } @@ -107,6 +119,17 @@ QSGNode* QskSliderSkinlet::updateSubNode( { return updateBoxNode( slider, node, QskSlider::Ripple ); } + + case LabelContainerRole: + { + return updateBoxNode( slider, node, QskSlider::LabelContainer ); + } + + case LabelTextRole: + { + const auto text = labelValue( slider ); + return updateTextNode( slider, node, text, QskSlider::LabelText ); + } } return Inherited::updateSubNode( skinnable, nodeRole, node ); @@ -263,6 +286,45 @@ QRectF QskSliderSkinlet::rippleRect( return r; } +QRectF QskSliderSkinlet::labelContainerRect( + const QskSlider* slider, const QRectF& rect ) const +{ + auto size = slider->strutSizeHint( QskSlider::LabelContainer ); + + if( size.isEmpty() ) + { + return {}; + } + + QFontMetricsF fm( slider->effectiveFont( QskSlider::LabelText ) ); + const auto w = qskHorizontalAdvance( fm, labelValue( slider ) ); + + const auto padding = slider->paddingHint( QskSlider::LabelContainer ); + const auto h = fm.height() + padding.top() + padding.bottom(); + + size = size.expandedTo( { w, h } ); + + const auto hr = subControlRect( slider, rect, QskSlider::Handle ); + const auto margins = slider->marginHint( QskSlider::LabelContainer ); + + qreal x, y; + + if( slider->orientation() == Qt::Horizontal ) + { + x = hr.center().x() - size.width() / 2; + y = hr.top() - margins.bottom() - size.height(); + } + else + { + x = hr.left() - size.width() - margins.left(); + y = hr.center().y() - size.height() / 2; + } + + const QRectF r( x, y, size.width(), size.height() ); + + return r; +} + QSizeF QskSliderSkinlet::sizeHint( const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const { @@ -282,4 +344,9 @@ QSizeF QskSliderSkinlet::sizeHint( const QskSkinnable* skinnable, return hint; } +QString QskSliderSkinlet::labelValue( const QskSlider* slider ) const +{ + return QString::number( slider->value(), 'f', 1 ); +} + #include "moc_QskSliderSkinlet.cpp" diff --git a/src/controls/QskSliderSkinlet.h b/src/controls/QskSliderSkinlet.h index 170e13c6..9306bd25 100644 --- a/src/controls/QskSliderSkinlet.h +++ b/src/controls/QskSliderSkinlet.h @@ -24,6 +24,8 @@ class QSK_EXPORT QskSliderSkinlet : public QskSkinlet FillRole, HandleRole, RippleRole, + LabelContainerRole, + LabelTextRole, RoleCount }; @@ -48,8 +50,11 @@ class QSK_EXPORT QskSliderSkinlet : public QskSkinlet QRectF handleRect( const QskSlider*, const QRectF& ) const; QRectF scaleRect( const QskSlider*, const QRectF& ) const; QRectF rippleRect( const QskSlider*, const QRectF& ) const; + QRectF labelContainerRect( const QskSlider*, const QRectF& ) const; QRectF innerRect( const QskSlider*, const QRectF&, QskAspect::Subcontrol ) const; + + QString labelValue( const QskSlider* ) const; }; #endif From f04dedd30a921e79f7b2478de4eaa4890ef30cc1 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Mon, 13 May 2024 16:59:47 +0200 Subject: [PATCH 3/4] QskSlider: Add stop indicators ... as required by M3 Resolves #391 --- designsystems/material3/QskMaterial3Skin.cpp | 23 ++ examples/gallery/inputs/InputPage.cpp | 17 +- src/controls/QskSlider.cpp | 2 + src/controls/QskSlider.h | 3 +- src/controls/QskSliderSkinlet.cpp | 209 ++++++++++++++----- src/controls/QskSliderSkinlet.h | 13 ++ 6 files changed, 211 insertions(+), 56 deletions(-) diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index a7b7c6c4..af27e2b2 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -865,8 +865,14 @@ void Editor::setupSlider() setBoxBorderMetrics( Q::Handle, 0 ); const QSizeF handleSize( 4_dp, 44_dp ); + const QSizeF handleSizeFocusedPressed( 2_dp, 44_dp ); setStrutSize( Q::Handle | A::Horizontal, handleSize ); + setStrutSize( Q::Handle | A::Horizontal, handleSizeFocusedPressed, + { QskStateCombination::Combination, Q::Focused | Q::Pressed } ); + setStrutSize( Q::Handle | A::Vertical, handleSize.transposed() ); + setStrutSize( Q::Handle | A::Vertical, handleSizeFocusedPressed.transposed(), + { QskStateCombination::Combination, Q::Focused | Q::Pressed } ); setGradient( Q::Handle, m_pal.primary ); setGradient( Q::Handle | Q::Pressed, m_pal.primary ); @@ -874,6 +880,23 @@ void Editor::setupSlider() const auto disabledColor = flattenedColor( m_pal.onSurface, m_pal.background, 0.38 ); setGradient( Q::Handle | Q::Disabled, disabledColor ); + for( auto indicator : { Q::GrooveStopIndicators, Q::FillStopIndicators } ) + { + setStrutSize( indicator, { 4_dp, 4_dp } ); + setBoxShape( indicator, 100, Qt::RelativeSize ); + } + + const auto p = 6_dp; + setPadding( Q::GrooveStopIndicators | A::Horizontal, { p, 0, p, 0 } ); + setPadding( Q::GrooveStopIndicators | A::Vertical, { 0, p, 0, p } ); + setPadding( Q::FillStopIndicators | A::Horizontal, { p, 0, p, 0 } ); + setPadding( Q::FillStopIndicators | A::Vertical, { 0, p, 0, p } ); + + setGradient( Q::GrooveStopIndicators, m_pal.primary ); + setGradient( Q::GrooveStopIndicators | Q::Disabled, m_pal.onSurface ); + setGradient( Q::FillStopIndicators, m_pal.secondaryContainer ); + setGradient( Q::FillStopIndicators | Q::Disabled, m_pal.inverseOnSurface ); + for( const auto state : { Q::Focused, Q::Pressed } ) { setStrutSize( Q::LabelContainer | state, 48_dp, 44_dp, diff --git a/examples/gallery/inputs/InputPage.cpp b/examples/gallery/inputs/InputPage.cpp index e5be225b..b224b54d 100644 --- a/examples/gallery/inputs/InputPage.cpp +++ b/examples/gallery/inputs/InputPage.cpp @@ -74,8 +74,19 @@ InputPage::InputPage( QQuickItem* parent ) : Page( Qt::Horizontal, parent ) { auto sliderH = new Slider( Qt::Horizontal ); + + auto discreteSliderH = new Slider( Qt::Horizontal ); + discreteSliderH->setSnap( true ); + discreteSliderH->setStepSize( 10 ); + discreteSliderH->setPageSize( 1 ); + auto sliderV = new Slider( Qt::Vertical ); + auto discreteSliderV = new Slider( Qt::Vertical ); + discreteSliderV->setSnap( true ); + discreteSliderV->setStepSize( 10 ); + discreteSliderV->setPageSize( 2 ); + auto inputBox = new InputBox(); auto gridBox = new QskGridBox( this ); @@ -83,8 +94,10 @@ InputPage::InputPage( QQuickItem* parent ) gridBox->setMargins( 30 ); gridBox->addItem( sliderV, 0, 0, -1, 1 ); - gridBox->addItem( sliderH, 0, 1, 1, -1 ); - gridBox->addItem( inputBox, 1, 1, -1, -1 ); + gridBox->addItem( discreteSliderV, 0, 1, -1, 1 ); + gridBox->addItem( sliderH, 0, 2, 1, -1 ); + gridBox->addItem( discreteSliderH, 1, 2, 1, -1 ); + gridBox->addItem( inputBox, 2, 2, -1, -1 ); auto inputs = findChildren< QskBoundedValueInput* >(); diff --git a/src/controls/QskSlider.cpp b/src/controls/QskSlider.cpp index fd865c03..6b2e70db 100644 --- a/src/controls/QskSlider.cpp +++ b/src/controls/QskSlider.cpp @@ -15,6 +15,8 @@ QSK_SUBCONTROL( QskSlider, Fill ) QSK_SUBCONTROL( QskSlider, Scale ) QSK_SUBCONTROL( QskSlider, Handle ) QSK_SUBCONTROL( QskSlider, Ripple ) +QSK_SUBCONTROL( QskSlider, GrooveStopIndicators ) +QSK_SUBCONTROL( QskSlider, FillStopIndicators ) QSK_SUBCONTROL( QskSlider, LabelContainer ) QSK_SUBCONTROL( QskSlider, LabelText ) diff --git a/src/controls/QskSlider.h b/src/controls/QskSlider.h index eb50c260..1bc675a4 100644 --- a/src/controls/QskSlider.h +++ b/src/controls/QskSlider.h @@ -25,7 +25,8 @@ class QSK_EXPORT QskSlider : public QskBoundedValueInput using Inherited = QskBoundedValueInput; public: - QSK_SUBCONTROLS( Panel, Groove, Fill, Scale, Handle, Ripple, LabelContainer, LabelText ) + QSK_SUBCONTROLS( Panel, Groove, Fill, Scale, Handle, Ripple, GrooveStopIndicators, FillStopIndicators, + LabelContainer, LabelText ) QSK_STATES( Pressed ) explicit QskSlider( QQuickItem* parent = nullptr ); diff --git a/src/controls/QskSliderSkinlet.cpp b/src/controls/QskSliderSkinlet.cpp index 7a1a161c..840d03e6 100644 --- a/src/controls/QskSliderSkinlet.cpp +++ b/src/controls/QskSliderSkinlet.cpp @@ -11,29 +11,53 @@ #include "QskFunctions.h" #include +#include -static inline QRectF qskInnerPanelRect( - const QskSlider* slider, const QRectF& contentsRect ) +using Q = QskSlider; + +namespace { - using Q = QskSlider; + inline QRectF qskInnerPanelRect( + const QskSlider* slider, const QRectF& contentsRect ) + { + #if 1 + auto padding = slider->paddingHint( Q::Panel ); + padding += slider->boxBorderMetricsHint( Q::Panel ).widths(); -#if 1 - auto padding = slider->paddingHint( Q::Panel ); - padding += slider->boxBorderMetricsHint( Q::Panel ).widths(); + auto r = slider->subControlRect( contentsRect, Q::Panel ); + r = r.marginsRemoved( padding ); + #else + r = slider->subControlContentsRect( contentsRect, Q::Panel ); + #endif - auto r = slider->subControlRect( contentsRect, Q::Panel ); - r = r.marginsRemoved( padding ); -#else - r = slider->subControlContentsRect( contentsRect, Q::Panel ); -#endif + return r; + } - return r; + QRectF qskInnerValueRect( const QskSlider* slider, const QRectF& contentsRect ) + { + // For M3 the stop indicators have some padding related to the groove (and fill), + // so we use the rect between first and last stop indicator as authoritative for + // indicators, handle etc. + const auto grooveIndicatorMargins = slider->paddingHint( Q::GrooveStopIndicators ); + const auto r = qskInnerPanelRect( slider, contentsRect ).marginsRemoved( grooveIndicatorMargins ); + return r; + } } QskSliderSkinlet::QskSliderSkinlet( QskSkin* skin ) : Inherited( skin ) { - setNodeRoles( { PanelRole, GrooveRole, FillRole, HandleRole, RippleRole, LabelContainerRole, LabelTextRole } ); + setNodeRoles( { + PanelRole, + GrooveRole, + FillRole, + FillStopIndicatorsRole, + GrooveStopIndicatorsRole, + HandleRole, + RippleRole, + LabelContainerRole, + LabelTextRole, + } ); } QskSliderSkinlet::~QskSliderSkinlet() @@ -45,42 +69,42 @@ QRectF QskSliderSkinlet::subControlRect( const QskSkinnable* skinnable, { const auto slider = static_cast< const QskSlider* >( skinnable ); - if ( subControl == QskSlider::Panel ) + if ( subControl == Q::Panel ) { return panelRect( slider, contentsRect ); } - if ( subControl == QskSlider::Groove ) + if ( subControl == Q::Groove ) { return grooveRect( slider, contentsRect ); } - if ( subControl == QskSlider::Fill ) + if ( subControl == Q::Fill ) { return fillRect( slider, contentsRect ); } - if ( subControl == QskSlider::Handle ) + if ( subControl == Q::Handle ) { return handleRect( slider, contentsRect ); } - if ( subControl == QskSlider::Scale ) + if ( subControl == Q::Scale ) { return scaleRect( slider, contentsRect ); } - if ( subControl == QskSlider::Ripple ) + if ( subControl == Q::Ripple ) { return rippleRect( slider, contentsRect ); } - if ( subControl == QskSlider::LabelContainer ) + if ( subControl == Q::LabelContainer ) { return labelContainerRect( slider, contentsRect ); } - if ( subControl == QskSlider::LabelText ) + if ( subControl == Q::LabelText ) { return labelContainerRect( slider, contentsRect ); } @@ -88,6 +112,68 @@ QRectF QskSliderSkinlet::subControlRect( const QskSkinnable* skinnable, return Inherited::subControlRect( skinnable, contentsRect, subControl ); } +int QskSliderSkinlet::sampleCount( const QskSkinnable* skinnable, QskAspect::Subcontrol subControl ) const +{ + const auto slider = static_cast< const QskSlider* >( skinnable ); + + if( slider->snap() ) + { + const auto num = qCeil( slider->boundaryLength() / slider->stepSize() ) + 1; + return num; + } + else + { + return ( subControl == Q::GrooveStopIndicators ) ? 1 : 0; + } +} + +QRectF QskSliderSkinlet::sampleRect( const QskSkinnable* skinnable, const QRectF& contentsRect, + QskAspect::Subcontrol subControl, int index ) const +{ + const auto slider = static_cast< const QskSlider* >( skinnable ); + + auto r = qskInnerValueRect( slider, contentsRect ); + + const auto size = slider->strutSizeHint( subControl ); + + const auto filledPoints = qFloor( ( slider->value() - slider->minimum() ) / slider->stepSize() ); + + if( slider->snap()) + { + if( slider->snap() + && ( ( index >= filledPoints && subControl == Q::FillStopIndicators ) + || ( index < filledPoints && subControl == Q::GrooveStopIndicators ) ) ) + { + return {}; + } + } + + const auto pos = slider->snap() ? slider->minimum() + index * slider->stepSize() : slider->maximum(); + + if( slider->orientation() == Qt::Horizontal ) + { + r.setTop( r.center().y() - size.height() / 2 ); + const auto x = r.left() + slider->valueAsRatio( pos ) * r.width() - size.width() / 2; + r.setLeft( x ); + } + else + { + r.setLeft( r.center().x() - size.width() / 2 ); + const auto y = r.bottom() - slider->valueAsRatio( pos ) * r.height() - size.height() / 2; + r.setTop( y ); + } + + r.setHeight( size.height() ); + r.setWidth( size.width() ); + + return r; +} + +QskAspect::States QskSliderSkinlet::sampleStates( const QskSkinnable*, QskAspect::Subcontrol, int ) const +{ + return {}; +} + QSGNode* QskSliderSkinlet::updateSubNode( const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const { @@ -97,53 +183,72 @@ QSGNode* QskSliderSkinlet::updateSubNode( { case PanelRole: { - return updateBoxNode( slider, node, QskSlider::Panel ); + return updateBoxNode( slider, node, Q::Panel ); } case GrooveRole: { - return updateBoxNode( slider, node, QskSlider::Groove ); + return updateBoxNode( slider, node, Q::Groove ); } case FillRole: { - return updateBoxNode( slider, node, QskSlider::Fill ); + return updateBoxNode( slider, node, Q::Fill ); + } + + case GrooveStopIndicatorsRole: + { + return updateSeriesNode( slider, Q::GrooveStopIndicators, node ); + } + + case FillStopIndicatorsRole: + { + return updateSeriesNode( slider, Q::FillStopIndicators, node ); } case HandleRole: { - return updateBoxNode( slider, node, QskSlider::Handle ); + return updateBoxNode( slider, node, Q::Handle ); } case RippleRole: { - return updateBoxNode( slider, node, QskSlider::Ripple ); + return updateBoxNode( slider, node, Q::Ripple ); } case LabelContainerRole: { - return updateBoxNode( slider, node, QskSlider::LabelContainer ); + return updateBoxNode( slider, node, Q::LabelContainer ); } case LabelTextRole: { const auto text = labelValue( slider ); - return updateTextNode( slider, node, text, QskSlider::LabelText ); + return updateTextNode( slider, node, text, Q::LabelText ); } } return Inherited::updateSubNode( skinnable, nodeRole, node ); } +QSGNode* QskSliderSkinlet::updateSampleNode( const QskSkinnable* skinnable, + QskAspect::Subcontrol subControl, int index, QSGNode* node ) const +{ + const auto slider = static_cast< const QskSlider* >( skinnable ); + const auto rect = sampleRect( slider, slider->contentsRect(), subControl, index ); + + return updateBoxNode( skinnable, node, rect, subControl ); +} + QRectF QskSliderSkinlet::panelRect( const QskSlider* slider, const QRectF& contentsRect ) const { auto r = contentsRect; - const qreal size = slider->metric( QskSlider::Panel | QskAspect::Size ); // 0: no hint + const qreal size = slider->metric( Q::Panel | QskAspect::Size ); // 0: no hint if ( size > 0 && size < r.height() ) { - const auto alignment = slider->alignmentHint( QskSlider::Panel ); + const auto alignment = slider->alignmentHint( Q::Panel ); if ( slider->orientation() == Qt::Horizontal ) r = qskAlignedRectF( r, r.width(), size, alignment & Qt::AlignVertical_Mask ); @@ -187,17 +292,17 @@ QRectF QskSliderSkinlet::grooveRect( const QskSlider* slider, const QRectF& contentsRect ) const { const auto r = qskInnerPanelRect( slider, contentsRect ); - auto grooveRect = innerRect( slider, contentsRect, QskSlider::Groove ); - const auto pos = qBound( 0.0, slider->handlePosition(), 1.0 ); + auto grooveRect = innerRect( slider, contentsRect, Q::Groove ); + const auto handleRect = slider->subControlRect( Q::Handle ); if ( slider->orientation() == Qt::Horizontal ) { - grooveRect.setLeft( r.left() + pos * r.width() ); + grooveRect.setLeft( handleRect.right() ); grooveRect.setRight( r.right() ); } else { - grooveRect.setBottom( r.bottom() - pos * r.height() ); + grooveRect.setBottom( handleRect.top() ); grooveRect.setTop( r.top() ); } @@ -207,25 +312,25 @@ QRectF QskSliderSkinlet::grooveRect( QRectF QskSliderSkinlet::scaleRect( const QskSlider* slider, const QRectF& rect ) const { - return innerRect( slider, rect, QskSlider::Groove ); + return innerRect( slider, rect, Q::Groove ); } QRectF QskSliderSkinlet::fillRect( const QskSlider* slider, const QRectF& contentsRect ) const { const auto r = qskInnerPanelRect( slider, contentsRect ); - const auto pos = qBound( 0.0, slider->handlePosition(), 1.0 ); + const auto handleRect = slider->subControlRect( Q::Handle ); - auto fillRect = innerRect( slider, contentsRect, QskSlider::Fill ); + auto fillRect = innerRect( slider, contentsRect, Q::Fill ); if ( slider->orientation() == Qt::Horizontal ) { fillRect.setLeft( r.left() ); - fillRect.setRight( r.left() + pos * r.width() ); + fillRect.setRight( handleRect.left() ); } else { fillRect.setBottom( r.bottom() ); - fillRect.setTop( r.bottom() - pos * r.height() ); + fillRect.setTop( handleRect.bottom() ); } return fillRect; @@ -234,12 +339,10 @@ QRectF QskSliderSkinlet::fillRect( QRectF QskSliderSkinlet::handleRect( const QskSlider* slider, const QRectF& contentsRect ) const { - using Q = QskSlider; - auto handleSize = slider->strutSizeHint( Q::Handle ); const auto pos = qBound( 0.0, slider->handlePosition(), 1.0 ); - const auto r = qskInnerPanelRect( slider, contentsRect ); + const auto r = qskInnerValueRect( slider, contentsRect ); auto center = r.center(); if ( slider->orientation() == Qt::Horizontal ) @@ -274,8 +377,8 @@ QRectF QskSliderSkinlet::handleRect( QRectF QskSliderSkinlet::rippleRect( const QskSlider* slider, const QRectF& rect ) const { - const auto rippleSize = slider->strutSizeHint( QskSlider::Ripple ); - const auto handleSize = slider->strutSizeHint( QskSlider::Handle ); + const auto rippleSize = slider->strutSizeHint( Q::Ripple ); + const auto handleSize = slider->strutSizeHint( Q::Handle ); const auto w = ( rippleSize.width() - handleSize.width() ) / 2; const auto h = ( rippleSize.height() - handleSize.height() ) / 2; @@ -289,23 +392,23 @@ QRectF QskSliderSkinlet::rippleRect( QRectF QskSliderSkinlet::labelContainerRect( const QskSlider* slider, const QRectF& rect ) const { - auto size = slider->strutSizeHint( QskSlider::LabelContainer ); + auto size = slider->strutSizeHint( Q::LabelContainer ); if( size.isEmpty() ) { return {}; } - QFontMetricsF fm( slider->effectiveFont( QskSlider::LabelText ) ); + QFontMetricsF fm( slider->effectiveFont( Q::LabelText ) ); const auto w = qskHorizontalAdvance( fm, labelValue( slider ) ); - const auto padding = slider->paddingHint( QskSlider::LabelContainer ); + const auto padding = slider->paddingHint( Q::LabelContainer ); const auto h = fm.height() + padding.top() + padding.bottom(); size = size.expandedTo( { w, h } ); - const auto hr = subControlRect( slider, rect, QskSlider::Handle ); - const auto margins = slider->marginHint( QskSlider::LabelContainer ); + const auto hr = subControlRect( slider, rect, Q::Handle ); + const auto margins = slider->marginHint( Q::LabelContainer ); qreal x, y; @@ -331,10 +434,10 @@ QSizeF QskSliderSkinlet::sizeHint( const QskSkinnable* skinnable, if ( which != Qt::PreferredSize ) return QSizeF(); - const auto panelHint = skinnable->strutSizeHint( QskSlider::Panel ); - const auto grooveHint = skinnable->strutSizeHint( QskSlider::Groove ); - const auto fillHint = skinnable->strutSizeHint( QskSlider::Fill ); - const auto handleHint = skinnable->strutSizeHint( QskSlider::Handle ); + const auto panelHint = skinnable->strutSizeHint( Q::Panel ); + const auto grooveHint = skinnable->strutSizeHint( Q::Groove ); + const auto fillHint = skinnable->strutSizeHint( Q::Fill ); + const auto handleHint = skinnable->strutSizeHint( Q::Handle ); auto hint = panelHint; hint = hint.expandedTo( grooveHint ); diff --git a/src/controls/QskSliderSkinlet.h b/src/controls/QskSliderSkinlet.h index 9306bd25..5f2b8040 100644 --- a/src/controls/QskSliderSkinlet.h +++ b/src/controls/QskSliderSkinlet.h @@ -22,6 +22,8 @@ class QSK_EXPORT QskSliderSkinlet : public QskSkinlet PanelRole, GrooveRole, FillRole, + GrooveStopIndicatorsRole, + FillStopIndicatorsRole, HandleRole, RippleRole, LabelContainerRole, @@ -39,10 +41,21 @@ class QSK_EXPORT QskSliderSkinlet : public QskSkinlet QSizeF sizeHint( const QskSkinnable*, Qt::SizeHint, const QSizeF& ) const override; + int sampleCount( const QskSkinnable*, QskAspect::Subcontrol ) const override; + + QRectF sampleRect( const QskSkinnable*, + const QRectF&, QskAspect::Subcontrol, int index ) const override; + + QskAspect::States sampleStates( const QskSkinnable*, + QskAspect::Subcontrol, int ) const override; + protected: QSGNode* updateSubNode( const QskSkinnable*, quint8 nodeRole, QSGNode* ) const override; + QSGNode* updateSampleNode( const QskSkinnable*, + QskAspect::Subcontrol, int index, QSGNode* ) const override; + private: QRectF panelRect( const QskSlider*, const QRectF& ) const; QRectF grooveRect( const QskSlider*, const QRectF& ) const; From b82097fdb414fa10a1076e6f0534b0b66e4851a5 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Wed, 29 May 2024 16:51:30 +0200 Subject: [PATCH 4/4] QskSlider: Hardcode minimum handle strut size (for now) ... so that it is easier to use with the finger Resolves #391 --- src/controls/QskSlider.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/controls/QskSlider.cpp b/src/controls/QskSlider.cpp index 6b2e70db..53c4b8d4 100644 --- a/src/controls/QskSlider.cpp +++ b/src/controls/QskSlider.cpp @@ -121,7 +121,15 @@ QSizeF QskSlider::handleSize() const QRectF QskSlider::handleRect() const { - return subControlRect( QskSlider::Handle ); + auto rect = subControlRect( Handle ); + +#if 1 // minimum handle strut size hardcoded here for now + const QSizeF strutSize( 60, 60 ); + const auto w = qMax( ( strutSize.width() - rect.width() ) / 2, 0.0 ); + const auto h = qMax( ( strutSize.height() - rect.height() ) / 2, 0.0 ); +#endif + + return rect.marginsAdded( { w, h, w, h } ); } void QskSlider::mousePressEvent( QMouseEvent* event )