diff --git a/designsystems/material3/CMakeLists.txt b/designsystems/material3/CMakeLists.txt index 7a7573f3..644837fc 100644 --- a/designsystems/material3/CMakeLists.txt +++ b/designsystems/material3/CMakeLists.txt @@ -8,12 +8,14 @@ list(APPEND HEADERS ) list(APPEND PRIVATE_HEADERS + QskMaterial3ProgressBarSkinlet.h QskMaterial3SliderSkinlet.h ) list(APPEND SOURCES QskMaterial3Skin.cpp QskMaterial3SkinFactory.cpp + QskMaterial3ProgressBarSkinlet.cpp QskMaterial3SliderSkinlet.cpp ) diff --git a/designsystems/material3/QskMaterial3ProgressBarSkinlet.cpp b/designsystems/material3/QskMaterial3ProgressBarSkinlet.cpp new file mode 100644 index 00000000..81622505 --- /dev/null +++ b/designsystems/material3/QskMaterial3ProgressBarSkinlet.cpp @@ -0,0 +1,96 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskMaterial3ProgressBarSkinlet.h" +#include +#include +#include +#include +#include +#include +#include + +using Q = QskProgressBar; + +QskMaterial3ProgressBarSkinlet::QskMaterial3ProgressBarSkinlet( QskSkin* skin ) + : Inherited( skin ) +{ + appendNodeRoles( { StopIndicatorRole } ); +} + +QSGNode* QskMaterial3ProgressBarSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + auto progressBar = static_cast< const QskProgressBar* >( skinnable ); + + switch( nodeRole ) + { + case GrooveRole: + { + auto clippedNode = QskSGNode::findChildNode( node, GrooveRole ); + clippedNode = Inherited::updateSubNode( skinnable, nodeRole, clippedNode ); + + if ( clippedNode == nullptr ) + return nullptr; + + auto clipNode = updateGrooveClipNode( progressBar, node ); + QskSGNode::setNodeRole( clippedNode, nodeRole ); + QskSGNode::setParentNode( clippedNode, clipNode ); + + return clipNode; + } + case StopIndicatorRole: + { + if ( !( progressBar->isIndeterminate() || progressBar->hasOrigin() ) ) + return updateStopIndicatorNode( progressBar, node ); + + return nullptr; + } + } + + return Inherited::updateSubNode( skinnable, nodeRole, node ); +} + +QSGNode* QskMaterial3ProgressBarSkinlet::updateStopIndicatorNode( + const QskProgressBar* progressBar, QSGNode* node ) const +{ + auto rect = progressBar->subControlRect( Q::Groove ); + if ( rect.isEmpty() ) + return nullptr; + + if( progressBar->orientation() == Qt::Horizontal ) + rect.setLeft( rect.right() - rect.height() ); + else + rect.setBottom( rect.top() + rect.width() ); + + const auto color = progressBar->gradientHint( Q::Fill ).endColor(); + const auto shape = progressBar->boxShapeHint( Q::Fill ); + + return updateBoxNode( progressBar, node, rect, shape, + QskBoxBorderMetrics(), QskBoxBorderColors(), color ); +} + +QSGNode* QskMaterial3ProgressBarSkinlet::updateGrooveClipNode( + const QskProgressBar* progressBar, QSGNode* node ) const +{ + auto rect = progressBar->subControlRect( Q::Fill ); + if ( rect.isEmpty() ) + return nullptr; + + QskMargins margins; + if ( progressBar->orientation() == Qt::Horizontal ) + margins.setMargins( rect.height(), 0.0 ); + else + margins.setMargins( 0.0, rect.width() ); + + rect = rect.marginsAdded( margins ); + + auto clipNode = QskSGNode::ensureNode< QskClipNode >( node ); + clipNode->setRegion( progressBar->subControlRect( Q::Groove ), rect ); + + return clipNode; +} + +#include "moc_QskMaterial3ProgressBarSkinlet.cpp" diff --git a/designsystems/material3/QskMaterial3ProgressBarSkinlet.h b/designsystems/material3/QskMaterial3ProgressBarSkinlet.h new file mode 100644 index 00000000..6ccd7d7c --- /dev/null +++ b/designsystems/material3/QskMaterial3ProgressBarSkinlet.h @@ -0,0 +1,37 @@ +/****************************************************************************** + * QSkinny - Copyright (C) The authors + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_MATERIAL3_PROGRESSBAR_SKINLET_H +#define QSK_MATERIAL3_PROGRESSBAR_SKINLET_H + +#include + +class QskProgressBar; + +class QskMaterial3ProgressBarSkinlet : QskProgressBarSkinlet +{ + Q_GADGET + + using Inherited = QskProgressBarSkinlet; + + public: + enum NodeRole + { + StopIndicatorRole = Inherited::RoleCount, + RoleCount + }; + + Q_INVOKABLE QskMaterial3ProgressBarSkinlet( QskSkin* = nullptr ); + + protected: + QSGNode* updateSubNode( const QskSkinnable*, + quint8 nodeRole, QSGNode* ) const override; + + private: + QSGNode* updateStopIndicatorNode( const QskProgressBar*, QSGNode* ) const; + QSGNode* updateGrooveClipNode( const QskProgressBar*, QSGNode* ) const; +}; + +#endif diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index db3306f1..e1d70428 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -9,6 +9,7 @@ */ #include "QskMaterial3Skin.h" +#include "QskMaterial3ProgressBarSkinlet.h" #include "QskMaterial3SliderSkinlet.h" #include @@ -487,20 +488,13 @@ 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 ); setGradient( Q::Fill, m_pal.primary ); @@ -1601,6 +1595,7 @@ qreal QskMaterial3Theme::stateOpacity( int state ) const QskMaterial3Skin::QskMaterial3Skin( QObject* parent ) : Inherited( parent ) { + declareSkinlet< QskProgressBar, QskMaterial3ProgressBarSkinlet >(); declareSkinlet< QskSlider, QskMaterial3SliderSkinlet >(); } diff --git a/src/controls/QskProgressBarSkinlet.cpp b/src/controls/QskProgressBarSkinlet.cpp index 225fb44c..f3b99a37 100644 --- a/src/controls/QskProgressBarSkinlet.cpp +++ b/src/controls/QskProgressBarSkinlet.cpp @@ -13,44 +13,76 @@ using Q = QskProgressBar; -namespace +static QskIntervalF qskFillInterval( const QskProgressIndicator* indicator ) { - QskIntervalF qskFillInterval( const QskProgressIndicator* indicator ) + qreal pos1, pos2; + + if ( indicator->isIndeterminate() ) { - qreal pos1, pos2; + const auto pos = indicator->positionHint( Q::Fill ); - if ( indicator->isIndeterminate() ) - { - const auto pos = indicator->positionHint( QskProgressIndicator::Fill ); + static const QEasingCurve curve( QEasingCurve::InOutCubic ); - static const QEasingCurve curve( QEasingCurve::InOutCubic ); + const qreal off = 0.15; - const qreal off = 0.15; - - pos1 = curve.valueForProgress( qMax( pos - off, 0.0 ) ); - pos2 = curve.valueForProgress( qMin( pos + off, 1.0 ) ); - } - else - { - pos1 = indicator->valueAsRatio( indicator->origin() ); - pos2 = indicator->valueAsRatio( indicator->value() ); - } - - auto bar = static_cast< const QskProgressBar* >( indicator ); - if( bar->orientation() == Qt::Horizontal ) - { - if ( bar->layoutMirroring() ) - { - pos1 = 1.0 - pos1; - pos2 = 1.0 - pos2; - } - } - - if ( pos1 > pos2 ) - std::swap( pos1, pos2 ); - - return QskIntervalF( pos1, pos2 ); + pos1 = curve.valueForProgress( qMax( pos - off, 0.0 ) ); + pos2 = curve.valueForProgress( qMin( pos + off, 1.0 ) ); } + else + { + pos1 = indicator->valueAsRatio( indicator->origin() ); + pos2 = indicator->valueAsRatio( indicator->value() ); + } + + auto bar = static_cast< const QskProgressBar* >( indicator ); + if( bar->orientation() == Qt::Horizontal ) + { + if ( bar->layoutMirroring() ) + { + pos1 = 1.0 - pos1; + pos2 = 1.0 - pos2; + } + } + + if ( pos1 > pos2 ) + std::swap( pos1, pos2 ); + + return QskIntervalF( pos1, pos2 ); +} + +static QskGradient qskFillGradient( const QskProgressBar* progressBar ) +{ + auto gradient = progressBar->gradientHint( Q::Fill ); + + if ( gradient.isVisible() && !gradient.isMonochrome() + && ( gradient.type() == QskGradient::Stops ) ) + { + /* + When having stops only we use a linear gradient, + where the colors are increasing in direction of the + progress value. We interprete the gradient as a + definition for the 100% situation and have to adjust + the stops for smaller bars. + + For this situation it would be more convenient to + adjust the start/stop positions, but the box renderer is + not supporting this yet. TODO ... + */ + + const auto intv = qskFillInterval( progressBar ); + + const auto stops = qskExtractedGradientStops( + gradient.stops(), intv.lowerBound(), intv.upperBound() ); + + gradient.setStops( stops ); + + gradient.setLinearDirection( progressBar->orientation() ); + + if ( progressBar->orientation() == Qt::Vertical || progressBar->layoutMirroring() ) + gradient.reverse(); + } + + return gradient; } QskProgressBarSkinlet::QskProgressBarSkinlet( QskSkin* skin ) @@ -69,28 +101,10 @@ 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; - } + return grooveRect( bar, contentsRect ); if( subControl == Q::Fill ) - { - return barRect( bar ); - } + return fillRect( bar ); return Inherited::subControlRect( skinnable, contentsRect, subControl ); } @@ -104,76 +118,61 @@ QSGNode* QskProgressBarSkinlet::updateGrooveNode( QSGNode* QskProgressBarSkinlet::updateFillNode( const QskProgressIndicator* indicator, QSGNode* node ) const { - const auto bar = static_cast< const Q* >( indicator ); - - const auto subControl = Q::Fill; - - const auto rect = indicator->subControlRect( subControl ); + const auto rect = indicator->subControlRect( Q::Fill ); if ( rect.isEmpty() ) return nullptr; - auto gradient = indicator->gradientHint( subControl ); - if ( !gradient.isVisible() ) - return nullptr; - - if ( ( gradient.type() == QskGradient::Stops ) && !gradient.isMonochrome() ) - { - /* - When having stops only we use a linear gradient, - where the colors are increasing in direction of the - progress value. We interprete the gradient as a - definition for the 100% situation and have to adjust - the stops for smaller bars. - - For this situation it would be more convenient to - adjust the start/stop positions, but the box renderer is - not supporting this yet. TODO ... - */ - - const auto intv = qskFillInterval( bar ); - - const auto stops = qskExtractedGradientStops( gradient.stops(), - intv.lowerBound(), intv.upperBound() ); - - gradient.setStops( stops ); - - gradient.setLinearDirection( static_cast< Qt::Orientation >( bar->orientation() ) ); - - if ( bar->orientation() == Qt::Vertical || bar->layoutMirroring() ) - gradient.reverse(); - } - - return updateBoxNode( indicator, node, rect, gradient, subControl ); + const auto progressBar = static_cast< const Q* >( indicator ); + return updateBoxNode( indicator, node, rect, + qskFillGradient( progressBar ), Q::Fill ); } -QRectF QskProgressBarSkinlet::barRect( const Q* bar ) const +QRectF QskProgressBarSkinlet::grooveRect( + const QskProgressBar* progressBar, const QRectF& contentsRect ) const { - const auto subControl = Q::Groove; + const auto size = progressBar->metric( Q::Groove | QskAspect::Size ); - const auto barSize = bar->metric( Q::Fill | QskAspect::Size ); - auto rect = bar->subControlRect( subControl ); - - if ( bar->orientation() == Qt::Horizontal ) + auto rect = contentsRect; + if ( progressBar->orientation() == Qt::Horizontal ) { - rect.setY( rect.y() + 0.5 * ( rect.height() - barSize ) ); - rect.setHeight( barSize ); + rect.setY( rect.y() + 0.5 * ( rect.height() - size ) ); + rect.setHeight( size ); } else { - rect.setX( rect.x() + 0.5 * ( rect.width() - barSize ) ); - rect.setWidth( barSize ); + rect.setX( rect.x() + 0.5 * ( rect.width() - size ) ); + rect.setWidth( size ); } - const auto borderMetrics = bar->boxBorderMetricsHint( subControl ); + return rect; +} - auto m = bar->paddingHint( subControl ); +QRectF QskProgressBarSkinlet::fillRect( const QskProgressBar* progressBar ) const +{ + const auto size = progressBar->metric( Q::Fill | QskAspect::Size ); + auto rect = progressBar->subControlRect( Q::Groove ); + + if ( progressBar->orientation() == Qt::Horizontal ) + { + rect.setY( rect.y() + 0.5 * ( rect.height() - size ) ); + rect.setHeight( size ); + } + else + { + rect.setX( rect.x() + 0.5 * ( rect.width() - size ) ); + rect.setWidth( size ); + } + + const auto borderMetrics = progressBar->boxBorderMetricsHint( Q::Groove ); + + auto m = progressBar->paddingHint( Q::Groove ); m += 0.5 * borderMetrics.toAbsolute( rect.size() ).widths(); rect = rect.marginsRemoved( m ); - const auto intv = qskFillInterval( bar ); + const auto intv = qskFillInterval( progressBar ); - if( bar->orientation() == Qt::Horizontal ) + if( progressBar->orientation() == Qt::Horizontal ) { const auto w = rect.width(); @@ -197,11 +196,12 @@ QSizeF QskProgressBarSkinlet::sizeHint( const QskSkinnable* skinnable, if ( which != Qt::PreferredSize ) return QSizeF(); - const auto bar = static_cast< const Q* >( skinnable ); + auto extent = skinnable->metric( Q::Groove | QskAspect::Size ); + extent = qMax( extent, skinnable->metric( Q::Fill | QskAspect::Size ) ); - const auto extent = bar->extent(); + const auto progressBar = static_cast< const QskProgressBar* >( skinnable ); - if ( bar->orientation() == Qt::Horizontal ) + if ( progressBar->orientation() == Qt::Horizontal ) return QSizeF( -1, extent ); else return QSizeF( extent, -1 ); diff --git a/src/controls/QskProgressBarSkinlet.h b/src/controls/QskProgressBarSkinlet.h index 581630bc..2ce543ae 100644 --- a/src/controls/QskProgressBarSkinlet.h +++ b/src/controls/QskProgressBarSkinlet.h @@ -31,7 +31,8 @@ class QSK_EXPORT QskProgressBarSkinlet : public QskProgressIndicatorSkinlet QSGNode* updateFillNode( const QskProgressIndicator*, QSGNode* ) const override; private: - QRectF barRect( const QskProgressBar* ) const; + QRectF fillRect( const QskProgressBar* ) const; + QRectF grooveRect( const QskProgressBar*, const QRectF& ) const; }; #endif diff --git a/src/controls/QskProgressIndicator.cpp b/src/controls/QskProgressIndicator.cpp index 2af580e0..8c7a507e 100644 --- a/src/controls/QskProgressIndicator.cpp +++ b/src/controls/QskProgressIndicator.cpp @@ -148,9 +148,7 @@ void QskProgressIndicator::resetExtent() qreal QskProgressIndicator::extent() const { - auto grooveSize = metric( Groove | QskAspect::Size ); - auto fillSize = metric( Fill | QskAspect::Size ); - return qMax( grooveSize, fillSize ); + return metric( Groove | QskAspect::Size ); } void QskProgressIndicator::setOrigin( qreal origin )