From 9a5dd886ed2445606dc7887e7d7024a588a967aa Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Thu, 21 Nov 2024 17:59:54 +0100 Subject: [PATCH] slider ticks - not only for M3 --- designsystems/material3/QskMaterial3Skin.cpp | 12 +- .../material3/QskMaterial3SliderSkinlet.cpp | 112 ++++++------------ .../material3/QskMaterial3SliderSkinlet.h | 7 +- src/common/QskNamespace.h | 8 ++ src/controls/QskSlider.cpp | 65 +++++++++- src/controls/QskSlider.h | 16 ++- src/controls/QskSliderSkinlet.cpp | 82 +++++++++++++ src/controls/QskSliderSkinlet.h | 12 ++ 8 files changed, 228 insertions(+), 86 deletions(-) diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index 092c4f9c2..746493bd5 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -927,15 +927,15 @@ void Editor::setupSlider() setBoxShape( Q::Groove, 100, Qt::RelativeSize ); setBoxShape( Q::Fill, 100, Qt::RelativeSize ); - setStrutSize( Q::Ticks, { 4_dp, 4_dp } ); - setBoxShape( Q::Ticks, 100, Qt::RelativeSize ); + setStrutSize( Q::Tick, { 4_dp, 4_dp } ); + setBoxShape( Q::Tick, 100, Qt::RelativeSize ); - setGradient( Q::Ticks, m_pal.primary ); - setGradient( Q::Ticks | Q::Disabled, m_pal.onSurface ); + setGradient( Q::Tick, m_pal.primary ); + setGradient( Q::Tick | Q::Disabled, m_pal.onSurface ); - setGradient( Q::Ticks | SK::Filled, m_pal.secondaryContainer, + setGradient( Q::Tick | SK::Filled, m_pal.secondaryContainer, { QskStateCombination::CombinationNoState, Q::Focused | Q::Pressed } ); - setGradient( Q::Ticks | SK::Filled | Q::Disabled, m_pal.inverseOnSurface ); + setGradient( Q::Tick | SK::Filled | Q::Disabled, m_pal.inverseOnSurface ); for ( const auto variation : { A::Horizontal, A::Vertical } ) { diff --git a/designsystems/material3/QskMaterial3SliderSkinlet.cpp b/designsystems/material3/QskMaterial3SliderSkinlet.cpp index 52126aa30..7a743b2d2 100644 --- a/designsystems/material3/QskMaterial3SliderSkinlet.cpp +++ b/designsystems/material3/QskMaterial3SliderSkinlet.cpp @@ -16,31 +16,9 @@ QSK_SYSTEM_STATE( QskMaterial3SliderSkinlet, Filled, QskAspect::FirstUserState > using Q = QskSlider; -static inline bool qskHasOrigin( const QskSlider* ) +static inline bool qskHasBoundaryTicks( const QskSlider* slider ) { - return false; // TODO -} - -static inline qreal qskTickValue( const QskSlider* slider, int index ) -{ - if( slider->isSnapping() && slider->stepSize() ) - return slider->minimum() + index * slider->stepSize(); - - if ( qskHasOrigin( slider ) ) - { - switch( index ) - { - case 0: - return slider->minimum(); - -#if 0 - case 1: - return slider->origin(); -#endif - } - } - - return slider->maximum(); + return ( slider->graduationPolicy() == Qsk::Maybe ) && !slider->isSnapping(); } QskMaterial3SliderSkinlet::QskMaterial3SliderSkinlet( QskSkin* skin ) @@ -82,7 +60,7 @@ QSGNode* QskMaterial3SliderSkinlet::updateSubNode( case GrooveRole: case FillRole: { - auto clippedNode = QskSGNode::findChildNode( node, nodeRole ); + auto clippedNode = QskSGNode::findChildNode( node, nodeRole ); clippedNode = Inherited::updateSubNode( skinnable, nodeRole, clippedNode ); if ( clippedNode ) @@ -102,11 +80,6 @@ QSGNode* QskMaterial3SliderSkinlet::updateSubNode( return nullptr; } - - case TicksRole: - { - return updateSeriesNode( skinnable, Q::Ticks, node ); - } } return Inherited::updateSubNode( skinnable, nodeRole, node ); @@ -115,48 +88,50 @@ QSGNode* QskMaterial3SliderSkinlet::updateSubNode( int QskMaterial3SliderSkinlet::sampleCount( const QskSkinnable* skinnable, QskAspect::Subcontrol subControl ) const { - if ( subControl == Q::Ticks ) + if ( subControl == Q::Tick ) { const auto slider = static_cast< const QskSlider* >( skinnable ); - if( slider->isSnapping() && slider->stepSize() ) - return qCeil( slider->boundaryLength() / slider->stepSize() ) + 1; + if ( qskHasBoundaryTicks( slider ) ) + { + const bool hasOrigin = false; - // min/origin/max or max - return qskHasOrigin( slider ) ? 3 : 1; + // min/origin/max or max + return hasOrigin ? 3 : 1; + } } return Inherited::sampleCount( skinnable, subControl ); } -QRectF QskMaterial3SliderSkinlet::sampleRect( - const QskSkinnable* skinnable, const QRectF& contentsRect, +QVariant QskMaterial3SliderSkinlet::sampleAt( const QskSkinnable* skinnable, QskAspect::Subcontrol subControl, int index ) const { - if ( subControl != Q::Ticks ) - return Inherited::sampleRect( skinnable, contentsRect, subControl, index ); - - const auto slider = static_cast< const QskSlider* >( skinnable ); + if ( subControl == Q::Tick ) + { + const auto slider = static_cast< const QskSlider* >( skinnable ); - const auto tickPos = slider->valueAsRatio( qskTickValue( slider, index ) ); + if ( qskHasBoundaryTicks( slider ) ) + { + switch( index ) + { + case 1: + return slider->minimum(); - const auto size = skinnable->strutSizeHint( Q::Ticks ); - const auto r = subControlRect( skinnable, contentsRect, Q::Scale ); + #if 0 + case 2: + return slider->origin(); + #endif - qreal x, y; + default: + return slider->maximum(); + } - if( slider->orientation() == Qt::Horizontal ) - { - x = tickPos * r.width() - 0.5 * size.width(); - y = 0.5 * ( r.height() - size.height() ); - } - else - { - y = r.height() - ( tickPos * r.height() ) - 0.5 * size.height(); - x = 0.5 * ( r.width() - size.width() ); + return QVariant(); + } } - return QRectF( r.x() + x, r.y() + y, size.width(), size.height() ); + return Inherited::sampleAt( skinnable, subControl, index ); } QskAspect::States QskMaterial3SliderSkinlet::sampleStates( @@ -164,28 +139,19 @@ QskAspect::States QskMaterial3SliderSkinlet::sampleStates( { auto states = Inherited::sampleStates( skinnable, subControl, index ); - if ( subControl == Q::Ticks ) - { - const auto slider = static_cast< const QskSlider* >( skinnable ); - if ( qskTickValue( slider, index ) <= slider->value() ) - states |= QskMaterial3SliderSkinlet::Filled; - } - - return states; -} - -QSGNode* QskMaterial3SliderSkinlet::updateSampleNode( const QskSkinnable* skinnable, - QskAspect::Subcontrol subControl, int index, QSGNode* node ) const -{ - if ( subControl == Q::Ticks ) + if ( subControl == Q::Tick ) { - const auto slider = static_cast< const QskSlider* >( skinnable ); - const auto rect = sampleRect( slider, slider->contentsRect(), subControl, index ); + const auto tickValue = sampleAt( skinnable, subControl, index ); + if ( tickValue.canConvert< qreal >() ) + { + const auto slider = static_cast< const QskSlider* >( skinnable ); - return updateBoxNode( skinnable, node, rect, subControl ); + if ( tickValue.value< qreal >() <= slider->value() ) + states |= QskMaterial3SliderSkinlet::Filled; + } } - return Inherited::updateSampleNode( skinnable, subControl, index, node ); + return states; } #include "moc_QskMaterial3SliderSkinlet.cpp" diff --git a/designsystems/material3/QskMaterial3SliderSkinlet.h b/designsystems/material3/QskMaterial3SliderSkinlet.h index eaa3bf9cd..b5e6f8a9b 100644 --- a/designsystems/material3/QskMaterial3SliderSkinlet.h +++ b/designsystems/material3/QskMaterial3SliderSkinlet.h @@ -24,8 +24,8 @@ class QskMaterial3SliderSkinlet : QskSliderSkinlet int sampleCount( const QskSkinnable*, QskAspect::Subcontrol ) const override; - QRectF sampleRect( const QskSkinnable*, - const QRectF&, QskAspect::Subcontrol, int index ) const override; + QVariant sampleAt( const QskSkinnable*, + QskAspect::Subcontrol, int index ) const override; QskAspect::States sampleStates( const QskSkinnable*, QskAspect::Subcontrol, int ) const override; @@ -33,9 +33,6 @@ class QskMaterial3SliderSkinlet : QskSliderSkinlet protected: QSGNode* updateSubNode( const QskSkinnable*, quint8 nodeRole, QSGNode* ) const override; - - QSGNode* updateSampleNode( const QskSkinnable*, - QskAspect::Subcontrol, int index, QSGNode* ) const override; }; #endif diff --git a/src/common/QskNamespace.h b/src/common/QskNamespace.h index 0129e81f4..92cc96f65 100644 --- a/src/common/QskNamespace.h +++ b/src/common/QskNamespace.h @@ -13,6 +13,14 @@ namespace Qsk { Q_NAMESPACE_EXPORT( QSK_EXPORT ) + enum Policy + { + Maybe, + Always, + Never + }; + Q_ENUM_NS( Policy ) + enum Direction { LeftToRight, diff --git a/src/controls/QskSlider.cpp b/src/controls/QskSlider.cpp index ef2461c96..60f5b8558 100644 --- a/src/controls/QskSlider.cpp +++ b/src/controls/QskSlider.cpp @@ -9,15 +9,42 @@ #include "QskIntervalF.h" #include "QskEvent.h" +#include + QSK_SUBCONTROL( QskSlider, Panel ) QSK_SUBCONTROL( QskSlider, Groove ) QSK_SUBCONTROL( QskSlider, Fill ) QSK_SUBCONTROL( QskSlider, Scale ) -QSK_SUBCONTROL( QskSlider, Ticks ) +QSK_SUBCONTROL( QskSlider, Tick ) QSK_SUBCONTROL( QskSlider, Handle ) QSK_SYSTEM_STATE( QskSlider, Pressed, QskAspect::FirstSystemState << 2 ) +static inline constexpr QskAspect qskAspectGraduationPolicy() +{ + return QskSlider::Tick | QskAspect::Option; +} + +static inline bool qskHasGraduation( const QskSlider* slider ) +{ + if ( slider->stepSize() ) + { + switch( slider->graduationPolicy() ) + { + case Qsk::Always: + return true; + + case Qsk::Maybe: + return slider->isSnapping(); + + case Qsk::Never: + return false; + } + } + + return false; +} + class QskSlider::PrivateData { public: @@ -90,6 +117,42 @@ QskAspect::Variation QskSlider::effectiveVariation() const return static_cast< QskAspect::Variation >( m_data->orientation ); } +void QskSlider::setGraduationPolicy( Qsk::Policy policy ) +{ + if ( setFlagHint( qskAspectGraduationPolicy(), policy ) ) + Q_EMIT graduationPolicyChanged( graduationPolicy() ); +} + +void QskSlider::resetGraduationPolicy() +{ + if ( resetSkinHint( qskAspectGraduationPolicy() ) ) + Q_EMIT graduationPolicyChanged( graduationPolicy() ); +} + +Qsk::Policy QskSlider::graduationPolicy() const +{ + return flagHint< Qsk::Policy >( qskAspectGraduationPolicy(), Qsk::Maybe ); +} + +QVector< qreal > QskSlider::visualGraduation() const +{ + QVector< qreal > graduation; + + if ( qskHasGraduation( this ) ) + { + const auto n = qCeil( boundaryLength() / stepSize() ) + 1; + + graduation.reserve( n ); + + for ( int i = 0; i < n - 1; i++ ) + graduation += minimum() + i * stepSize(); + + graduation += maximum(); + } + + return graduation; +} + void QskSlider::setTracking( bool on ) { if ( on != m_data->tracking ) diff --git a/src/controls/QskSlider.h b/src/controls/QskSlider.h index 616e59e19..fc89eac7f 100644 --- a/src/controls/QskSlider.h +++ b/src/controls/QskSlider.h @@ -7,6 +7,9 @@ #define QSK_SLIDER_H #include "QskBoundedValueInput.h" +#include "QskNamespace.h" + +#include class QSK_EXPORT QskSlider : public QskBoundedValueInput { @@ -14,6 +17,10 @@ class QSK_EXPORT QskSlider : public QskBoundedValueInput Q_PROPERTY( bool isPressed READ isPressed NOTIFY pressedChanged ) + Q_PROPERTY( Qsk::Policy graduationPolicy READ graduationPolicy + WRITE setGraduationPolicy RESET resetGraduationPolicy + NOTIFY graduationPolicyChanged ) + Q_PROPERTY( Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged ) @@ -25,7 +32,7 @@ class QSK_EXPORT QskSlider : public QskBoundedValueInput using Inherited = QskBoundedValueInput; public: - QSK_SUBCONTROLS( Panel, Groove, Fill, Scale, Ticks, Handle ) + QSK_SUBCONTROLS( Panel, Groove, Fill, Scale, Tick, Handle ) QSK_STATES( Pressed ) explicit QskSlider( QQuickItem* parent = nullptr ); @@ -38,6 +45,12 @@ class QSK_EXPORT QskSlider : public QskBoundedValueInput void setOrientation( Qt::Orientation ); Qt::Orientation orientation() const; + void setGraduationPolicy( Qsk::Policy ); + void resetGraduationPolicy(); + Qsk::Policy graduationPolicy() const; + + virtual QVector< qreal > visualGraduation() const; + void setTracking( bool ); bool isTracking() const; @@ -49,6 +62,7 @@ class QSK_EXPORT QskSlider : public QskBoundedValueInput void pressedChanged( bool ); void orientationChanged( Qt::Orientation ); void trackingChanged( bool ); + void graduationPolicyChanged( Qsk::Policy ); protected: void mousePressEvent( QMouseEvent* ) override; diff --git a/src/controls/QskSliderSkinlet.cpp b/src/controls/QskSliderSkinlet.cpp index 6c8474fc3..7d2c6ed4d 100644 --- a/src/controls/QskSliderSkinlet.cpp +++ b/src/controls/QskSliderSkinlet.cpp @@ -89,11 +89,65 @@ QSGNode* QskSliderSkinlet::updateSubNode( case HandleRole: return updateBoxNode( slider, node, Q::Handle ); + + case TicksRole: + return updateSeriesNode( slider, Q::Tick, node ); } return Inherited::updateSubNode( skinnable, nodeRole, node ); } +int QskSliderSkinlet::sampleCount( const QskSkinnable* skinnable, + QskAspect::Subcontrol subControl ) const +{ + if ( subControl == Q::Tick ) + { + const auto slider = static_cast< const QskSlider* >( skinnable ); + return slider->visualGraduation().count(); + } + + return Inherited::sampleCount( skinnable, subControl ); +} + +QVariant QskSliderSkinlet::sampleAt( const QskSkinnable* skinnable, + QskAspect::Subcontrol subControl, int index ) const +{ + if ( subControl == Q::Tick ) + { + const auto slider = static_cast< const QskSlider* >( skinnable ); + return slider->visualGraduation().value( index ); + } + + return Inherited::sampleAt( skinnable, subControl, index ); +} + +QRectF QskSliderSkinlet::sampleRect( + const QskSkinnable* skinnable, const QRectF& contentsRect, + QskAspect::Subcontrol subControl, int index ) const +{ + if ( subControl == Q::Tick ) + { + const auto slider = static_cast< const QskSlider* >( skinnable ); + return tickRect( slider, contentsRect, index ); + } + + return Inherited::sampleRect( skinnable, contentsRect, subControl, index ); +} + +QSGNode* QskSliderSkinlet::updateSampleNode( const QskSkinnable* skinnable, + QskAspect::Subcontrol subControl, int index, QSGNode* node ) const +{ + if ( subControl == Q::Tick ) + { + const auto slider = static_cast< const QskSlider* >( skinnable ); + const auto rect = sampleRect( slider, slider->contentsRect(), subControl, index ); + + return updateBoxNode( skinnable, node, rect, subControl ); + } + + return Inherited::updateSampleNode( skinnable, subControl, index, node ); +} + QRectF QskSliderSkinlet::panelRect( const QskSlider* slider, const QRectF& contentsRect ) const { @@ -166,6 +220,34 @@ QRectF QskSliderSkinlet::handleRect( return handleRect; } +QRectF QskSliderSkinlet::tickRect( const QskSlider* slider, + const QRectF& contentsRect, int index ) const +{ + const auto tickValue = sampleAt( slider, Q::Tick, index ); + if ( !tickValue.canConvert< qreal >() ) + return QRectF(); + + const auto tickPos = slider->valueAsRatio( tickValue.value< qreal >() ); + + const auto size = slider->strutSizeHint( Q::Tick ); + const auto r = subControlRect( slider, contentsRect, Q::Scale ); + + qreal x, y; + + if( slider->orientation() == Qt::Horizontal ) + { + x = tickPos * r.width() - 0.5 * size.width(); + y = 0.5 * ( r.height() - size.height() ); + } + else + { + y = r.height() - ( tickPos * r.height() ) - 0.5 * size.height(); + x = 0.5 * ( r.width() - size.width() ); + } + + return QRectF( r.x() + x, r.y() + y, size.width(), size.height() ); +} + QSizeF QskSliderSkinlet::sizeHint( const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const { diff --git a/src/controls/QskSliderSkinlet.h b/src/controls/QskSliderSkinlet.h index b88f4af87..192e366b5 100644 --- a/src/controls/QskSliderSkinlet.h +++ b/src/controls/QskSliderSkinlet.h @@ -37,14 +37,26 @@ 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; + + QVariant sampleAt( const QskSkinnable*, + QskAspect::Subcontrol, int index ) 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 fillRect( const QskSlider*, const QRectF& ) const; QRectF handleRect( const QskSlider*, const QRectF& ) const; + QRectF tickRect( const QskSlider*, const QRectF&, int index ) const; }; #endif