diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index ddb3c3b9..a6602db3 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -486,22 +486,21 @@ void Editor::setupProgressBar() using A = QskAspect; using Q = QskProgressBar; - auto size = 4_dp; - for ( auto subControl : { Q::Groove, Q::Fill } ) { - setMetric( subControl | A::Size, size ); - setPadding( subControl, 0 ); - - setBoxShape( subControl, 0 ); - setBoxBorderMetrics( subControl, 0 ); + setBoxShape( subControl, { 100, Qt::RelativeSize } ); + setMetric( subControl | A::Size, 4_dp ); } - setMetric( Q::Groove | A::Size, size ); setGradient( Q::Groove, m_pal.surfaceContainerHighest ); - setGradient( Q::Groove | Q::Disabled, m_pal.onSurface12 ); + setStrutSize( Q::GrooveStopIndicator, { 4_dp, 4_dp } ); + setBoxShape( Q::GrooveStopIndicator, { 100, Qt::RelativeSize } ); + setGradient( Q::GrooveStopIndicator, m_pal.primary ); + setGradient( Q::GrooveStopIndicator | Q::Disabled, m_pal.onSurface38 ); + + setSpacing( Q::Fill, 4_dp ); setGradient( Q::Fill, m_pal.primary ); setGradient( Q::Fill | Q::Disabled, m_pal.onSurface38 ); } @@ -510,9 +509,15 @@ void Editor::setupProgressRing() { using Q = QskProgressRing; + setArcMetrics( Q::Groove, 90, -360, 4_dp ); + setGradient( Q::Groove, m_pal.surfaceContainerHighest ); + setGradient( Q::Groove | Q::Disabled, m_pal.onSurface12 ); + + setSpacing( Q::Fill, 10 ); setStrutSize( Q::Fill, { 48_dp, 48_dp } ); - setGradient( Q::Fill, m_pal.primary ); setArcMetrics( Q::Fill, 90, -360, 4_dp ); + setGradient( Q::Fill, m_pal.primary ); + setGradient( Q::Fill | Q::Disabled, m_pal.onSurface38 ); } void Editor::setupRadioBox() diff --git a/src/controls/QskProgressBar.cpp b/src/controls/QskProgressBar.cpp index 16b17f63..ff75b47d 100644 --- a/src/controls/QskProgressBar.cpp +++ b/src/controls/QskProgressBar.cpp @@ -9,6 +9,7 @@ QSK_SUBCONTROL( QskProgressBar, Groove ) QSK_SUBCONTROL( QskProgressBar, Fill ) +QSK_SUBCONTROL( QskProgressBar, GrooveStopIndicator ) class QskProgressBar::PrivateData { diff --git a/src/controls/QskProgressBar.h b/src/controls/QskProgressBar.h index df844df8..21be807e 100644 --- a/src/controls/QskProgressBar.h +++ b/src/controls/QskProgressBar.h @@ -18,7 +18,7 @@ class QSK_EXPORT QskProgressBar : public QskProgressIndicator using Inherited = QskProgressIndicator; public: - QSK_SUBCONTROLS( Groove, Fill ) + QSK_SUBCONTROLS( Groove, Fill, GrooveStopIndicator ) QskProgressBar( Qt::Orientation, QQuickItem* parent = nullptr ); QskProgressBar( Qt::Orientation, qreal min, qreal max, QQuickItem* parent = nullptr ); diff --git a/src/controls/QskProgressBarSkinlet.cpp b/src/controls/QskProgressBarSkinlet.cpp index 225fb44c..d13c0264 100644 --- a/src/controls/QskProgressBarSkinlet.cpp +++ b/src/controls/QskProgressBarSkinlet.cpp @@ -15,6 +15,26 @@ using Q = QskProgressBar; namespace { + QRectF qskFullGrooveRect( const QskProgressBar* bar ) + { + const auto grooveSize = bar->metric( Q::Groove | QskAspect::Size ); + + auto rect = bar->contentsRect(); + + if ( bar->orientation() == Qt::Horizontal ) + { + rect.setY( rect.y() + 0.5 * ( rect.height() - grooveSize ) ); + rect.setHeight( grooveSize ); + } + else + { + rect.setX( rect.x() + 0.5 * ( rect.width() - grooveSize ) ); + rect.setWidth( grooveSize ); + } + + return rect; + } + QskIntervalF qskFillInterval( const QskProgressIndicator* indicator ) { qreal pos1, pos2; @@ -37,6 +57,7 @@ namespace } auto bar = static_cast< const QskProgressBar* >( indicator ); + if( bar->orientation() == Qt::Horizontal ) { if ( bar->layoutMirroring() ) @@ -51,11 +72,17 @@ namespace return QskIntervalF( pos1, pos2 ); } + + inline bool qskIsContiguous( const QskProgressBar* bar ) + { + return qFuzzyIsNull( bar->spacingHint( Q::Fill ) ); + } } QskProgressBarSkinlet::QskProgressBarSkinlet( QskSkin* skin ) : Inherited( skin ) { + setNodeRoles( { GrooveRole, FillRole, GrooveStopIndicatorRole } ); } QskProgressBarSkinlet::~QskProgressBarSkinlet() @@ -68,37 +95,46 @@ QRectF QskProgressBarSkinlet::subControlRect( { const auto bar = static_cast< const Q* >( skinnable ); - if( subControl == Q::Groove ) - { - const auto grooveSize = bar->metric( Q::Groove | QskAspect::Size ); - - auto rect = contentsRect; - if ( bar->orientation() == Qt::Horizontal ) - { - rect.setY( rect.y() + 0.5 * ( rect.height() - grooveSize ) ); - rect.setHeight( grooveSize ); - } - else - { - rect.setX( rect.x() + 0.5 * ( rect.width() - grooveSize ) ); - rect.setWidth( grooveSize ); - } - - return rect; - } - if( subControl == Q::Fill ) { return barRect( bar ); } + if( subControl == Q::GrooveStopIndicator ) + { + return grooveStopIndicatorRect( bar ); + } + return Inherited::subControlRect( skinnable, contentsRect, subControl ); } +QSGNode* QskProgressBarSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + const auto bar = static_cast< const Q* >( skinnable ); + + switch( nodeRole ) + { + case GrooveStopIndicatorRole: + { + if( bar->isIndeterminate() || bar->hasOrigin() ) + { + return nullptr; + } + else + { + return updateBoxNode( skinnable, node, Q::GrooveStopIndicator ); + } + } + } + + return Inherited::updateSubNode( skinnable, nodeRole, node ); +} + QSGNode* QskProgressBarSkinlet::updateGrooveNode( const QskProgressIndicator* indicator, QSGNode* node ) const { - return updateBoxNode( indicator, node, Q::Groove ); + return updateSeriesNode( indicator, Q::Groove, node ); } QSGNode* QskProgressBarSkinlet::updateFillNode( @@ -146,12 +182,21 @@ QSGNode* QskProgressBarSkinlet::updateFillNode( return updateBoxNode( indicator, node, rect, gradient, subControl ); } +QSGNode* QskProgressBarSkinlet::updateSampleNode( const QskSkinnable* skinnable, + QskAspect::Subcontrol subControl, int index, QSGNode* node ) const +{ + const auto bar = static_cast< const QskProgressBar* >( skinnable ); + const auto rect = sampleRect( bar, bar->contentsRect(), subControl, index ); + + return updateBoxNode( skinnable, node, rect, subControl ); +} + QRectF QskProgressBarSkinlet::barRect( const Q* bar ) const { const auto subControl = Q::Groove; const auto barSize = bar->metric( Q::Fill | QskAspect::Size ); - auto rect = bar->subControlRect( subControl ); + auto rect = qskFullGrooveRect( bar ); if ( bar->orientation() == Qt::Horizontal ) { @@ -207,4 +252,70 @@ QSizeF QskProgressBarSkinlet::sizeHint( const QskSkinnable* skinnable, return QSizeF( extent, -1 ); } +int QskProgressBarSkinlet::sampleCount( const QskSkinnable* skinnable, QskAspect::Subcontrol ) const +{ + const auto bar = static_cast< const Q* >( skinnable ); + const auto samples = ( ( bar->isIndeterminate() || bar->hasOrigin() ) && !qskIsContiguous( bar ) ) ? 2 : 1; + return samples; +} + +QRectF QskProgressBarSkinlet::sampleRect( const QskSkinnable* skinnable, + const QRectF&, QskAspect::Subcontrol, int index ) const +{ + const auto bar = static_cast< const Q* >( skinnable ); + const auto br = barRect( bar ); + QRectF rect = qskFullGrooveRect( bar ); + const auto spacing = bar->spacingHint( Q::Fill ); + const auto isContiguous = qskIsContiguous( bar ); + + if( isContiguous ) + { + return rect; + } + else + { + if( bar->orientation() == Qt::Horizontal ) + { + if( index == 0 ) + { + rect.setLeft( br.right() + spacing ); + } + else + { + rect.setRight( br.left() - spacing ); + } + } + else + { + if( index == 0 ) + { + rect.setBottom( br.top() - spacing ); + } + else + { + rect.setTop( br.bottom() + spacing ); + } + } + } + + return rect; +} + +QRectF QskProgressBarSkinlet::grooveStopIndicatorRect( const QskProgressBar* bar ) const +{ + auto rect = qskFullGrooveRect( bar ); + const auto size = bar->strutSizeHint( Q::GrooveStopIndicator ); + + if( bar->orientation() == Qt::Horizontal ) + { + rect.setLeft( rect.right() - size.width() ); + } + else + { + rect.setBottom( rect.top() + size.height() ); + } + + return rect; +} + #include "moc_QskProgressBarSkinlet.cpp" diff --git a/src/controls/QskProgressBarSkinlet.h b/src/controls/QskProgressBarSkinlet.h index 581630bc..cc7d6d0b 100644 --- a/src/controls/QskProgressBarSkinlet.h +++ b/src/controls/QskProgressBarSkinlet.h @@ -17,6 +17,11 @@ class QSK_EXPORT QskProgressBarSkinlet : public QskProgressIndicatorSkinlet using Inherited = QskProgressIndicatorSkinlet; public: + enum NodeRole + { + GrooveStopIndicatorRole = Inherited::RoleCount, + }; + Q_INVOKABLE QskProgressBarSkinlet( QskSkin* = nullptr ); ~QskProgressBarSkinlet() override; @@ -26,12 +31,23 @@ class QSK_EXPORT QskProgressBarSkinlet : public QskProgressIndicatorSkinlet 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; + protected: + QSGNode* updateSubNode( const QskSkinnable*, quint8 nodeRole, QSGNode* ) const override; + QSGNode* updateGrooveNode( const QskProgressIndicator*, QSGNode* ) const override; QSGNode* updateFillNode( const QskProgressIndicator*, QSGNode* ) const override; + QSGNode* updateSampleNode( const QskSkinnable*, + QskAspect::Subcontrol, int index, QSGNode* ) const override; + private: QRectF barRect( const QskProgressBar* ) const; + QRectF grooveStopIndicatorRect( const QskProgressBar* ) const; }; #endif diff --git a/src/controls/QskProgressIndicator.cpp b/src/controls/QskProgressIndicator.cpp index 7dd8d11e..2af580e0 100644 --- a/src/controls/QskProgressIndicator.cpp +++ b/src/controls/QskProgressIndicator.cpp @@ -189,6 +189,11 @@ qreal QskProgressIndicator::origin() const return minimum(); } +bool QskProgressIndicator::hasOrigin() const +{ + return m_data->hasOrigin; +} + void QskProgressIndicator::setValue( qreal value ) { if ( isComponentComplete() ) diff --git a/src/controls/QskProgressIndicator.h b/src/controls/QskProgressIndicator.h index 4df628f7..f8f1e919 100644 --- a/src/controls/QskProgressIndicator.h +++ b/src/controls/QskProgressIndicator.h @@ -51,6 +51,7 @@ class QSK_EXPORT QskProgressIndicator : public QskBoundedControl void resetOrigin(); qreal origin() const; + bool hasOrigin() const; qreal value() const; qreal valueAsRatio() const; // [0.0, 1.0] diff --git a/src/controls/QskProgressRingSkinlet.cpp b/src/controls/QskProgressRingSkinlet.cpp index 167cb861..0e47a830 100644 --- a/src/controls/QskProgressRingSkinlet.cpp +++ b/src/controls/QskProgressRingSkinlet.cpp @@ -10,6 +10,49 @@ using Q = QskProgressRing; +namespace +{ + inline bool qskIsContiguous( const QskProgressRing* ring ) + { + return qFuzzyIsNull( ring->spacingHint( Q::Fill ) ); + } + + QskIntervalF fillInterval( const QskProgressIndicator* indicator ) + { + qreal pos1, pos2; + + if ( indicator->isIndeterminate() ) + { + pos1 = pos2 = indicator->positionHint( QskProgressIndicator::Fill ); + } + else + { + pos1 = indicator->valueAsRatio( indicator->origin() ); + pos2 = indicator->valueAsRatio( indicator->value() ); + } + + if ( pos1 > pos2 ) + std::swap( pos1, pos2 ); + + return QskIntervalF( pos1, pos2 ); + } + + inline QskArcMetrics fillAngles( const QskProgressRing* ring ) + { + const auto intv = fillInterval( ring ); + + const auto metrics = ring->arcMetricsHint( Q::Fill ); + + if ( metrics.isNull() ) + return {}; + + const auto startAngle = metrics.startAngle() + intv.lowerBound() * metrics.spanAngle(); + const auto spanAngle = intv.upperBound() * metrics.spanAngle(); + + return QskArcMetrics( startAngle, spanAngle, 0 ); + } +} + QskProgressRingSkinlet::QskProgressRingSkinlet( QskSkin* skin ) : Inherited( skin ) { @@ -32,7 +75,22 @@ QRectF QskProgressRingSkinlet::subControlRect( QSGNode* QskProgressRingSkinlet::updateGrooveNode( const QskProgressIndicator* indicator, QSGNode* node ) const { - return updateArcNode( indicator, node, Q::Groove ); + const auto ring = static_cast< const Q* >( indicator ); + + if( qskIsContiguous( ring ) ) + { + return updateArcNode( indicator, node, Q::Groove ); + } + else + { + const auto angles = fillAngles( ring ); + const auto spacing = ring->spacingHint( Q::Fill ); + const auto sign = ( angles.spanAngle() > 0 ) ? 1 : -1; + const auto startAngle = angles.endAngle() + sign * spacing; + const auto spanAngle = sign * 360 - angles.spanAngle() - sign * 2 * spacing; + + return updateArcNode( ring, node, startAngle, spanAngle, Q::Groove ); + } } QSGNode* QskProgressRingSkinlet::updateFillNode( @@ -67,10 +125,9 @@ QSGNode* QskProgressRingSkinlet::updateFillNode( gradient.reverse(); } - const auto startAngle = metrics.startAngle() + intv.lowerBound() * metrics.spanAngle(); - const auto spanAngle = intv.upperBound() * metrics.spanAngle(); + const auto angles = fillAngles( ring ); - return updateArcNode( ring, node, rect, gradient, startAngle, spanAngle, subControl ); + return updateArcNode( ring, node, rect, gradient, angles.startAngle(), angles.spanAngle(), subControl ); } QSizeF QskProgressRingSkinlet::sizeHint( const QskSkinnable* skinnable, @@ -95,25 +152,4 @@ QSizeF QskProgressRingSkinlet::sizeHint( const QskSkinnable* skinnable, return hint; } -QskIntervalF QskProgressRingSkinlet::fillInterval( - const QskProgressIndicator* indicator ) const -{ - qreal pos1, pos2; - - if ( indicator->isIndeterminate() ) - { - pos1 = pos2 = indicator->positionHint( QskProgressIndicator::Fill ); - } - else - { - pos1 = indicator->valueAsRatio( indicator->origin() ); - pos2 = indicator->valueAsRatio( indicator->value() ); - } - - if ( pos1 > pos2 ) - std::swap( pos1, pos2 ); - - return QskIntervalF( pos1, pos2 ); -} - #include "moc_QskProgressRingSkinlet.cpp" diff --git a/src/controls/QskProgressRingSkinlet.h b/src/controls/QskProgressRingSkinlet.h index c5dc815b..d7171deb 100644 --- a/src/controls/QskProgressRingSkinlet.h +++ b/src/controls/QskProgressRingSkinlet.h @@ -29,8 +29,6 @@ class QSK_EXPORT QskProgressRingSkinlet : public QskProgressIndicatorSkinlet protected: QSGNode* updateGrooveNode( const QskProgressIndicator*, QSGNode* ) const override; QSGNode* updateFillNode( const QskProgressIndicator*, QSGNode* ) const override; - - QskIntervalF fillInterval( const QskProgressIndicator* ) const; }; #endif