From ec61c11ab64c8701f5f3693d8a985c83238c8758 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Tue, 25 Jul 2023 08:56:21 +0200 Subject: [PATCH] progress bars: Introduce progress ring and refactor Most of the code between rings and bars can be shared with a common superclass QskProgressIndicator. Resolves #98 --- .../gallery/progressbar/ProgressBarPage.cpp | 89 +++++- .../gallery/progressbar/ProgressBarPage.h | 4 + examples/iotdashboard/Skin.cpp | 1 - examples/iotdashboard/StoragePage.cpp | 4 - qmlexport/QskQml.cpp | 2 + skins/fluent2/QskFluent2Skin.cpp | 43 ++- skins/material3/QskMaterial3Skin.cpp | 28 +- skins/material3/QskMaterial3Skin.h | 2 + skins/squiek/QskSquiekSkin.cpp | 29 +- src/CMakeLists.txt | 16 +- src/controls/QskProgressBar.cpp | 235 +--------------- src/controls/QskProgressBar.h | 62 +---- src/controls/QskProgressBarSkinlet.cpp | 113 ++++---- src/controls/QskProgressBarSkinlet.h | 19 +- src/controls/QskProgressIndicator.cpp | 255 ++++++++++++++++++ src/controls/QskProgressIndicator.h | 83 ++++++ src/controls/QskProgressIndicatorSkinlet.cpp | 40 +++ src/controls/QskProgressIndicatorSkinlet.h | 37 +++ src/controls/QskProgressRing.cpp | 76 ++++++ src/controls/QskProgressRing.h | 50 ++++ src/controls/QskProgressRingSkinlet.cpp | 125 +++++++++ src/controls/QskProgressRingSkinlet.h | 34 +++ src/controls/QskSkin.cpp | 4 + 23 files changed, 967 insertions(+), 384 deletions(-) create mode 100644 src/controls/QskProgressIndicator.cpp create mode 100644 src/controls/QskProgressIndicator.h create mode 100644 src/controls/QskProgressIndicatorSkinlet.cpp create mode 100644 src/controls/QskProgressIndicatorSkinlet.h create mode 100644 src/controls/QskProgressRing.cpp create mode 100644 src/controls/QskProgressRing.h create mode 100644 src/controls/QskProgressRingSkinlet.cpp create mode 100644 src/controls/QskProgressRingSkinlet.h diff --git a/examples/gallery/progressbar/ProgressBarPage.cpp b/examples/gallery/progressbar/ProgressBarPage.cpp index fe1faee4..8655343b 100644 --- a/examples/gallery/progressbar/ProgressBarPage.cpp +++ b/examples/gallery/progressbar/ProgressBarPage.cpp @@ -4,13 +4,18 @@ *****************************************************************************/ #include "ProgressBarPage.h" -#include + +#include #include #include #include #include +#include +#include #include +#include + namespace { class ProgressBar : public QskProgressBar @@ -35,9 +40,32 @@ namespace colors += hctColor.toned( 45 ).rgb(); colors += hctColor.toned( 30 ).rgb(); - setBarGradient( qskBuildGradientStops( colors, true ) ); + setFillGradient( qskBuildGradientStops( colors, true ) ); } }; + + class DeterminateIndicatorsAnimator : public QskAnimator + { + public: + DeterminateIndicatorsAnimator( const QVector< QskProgressIndicator* >& indicators ) + : QskAnimator() + , m_indicators( indicators ) + { + setAutoRepeat( true ); + setDuration( 3000 ); + } + + void advance( qreal value ) override + { + for( auto* indicator : m_indicators ) + { + indicator->setValueAsRatio( value ); + } + } + + private: + const QVector< QskProgressIndicator* > m_indicators; + }; } ProgressBarPage::ProgressBarPage( QQuickItem* parent ) @@ -53,10 +81,7 @@ void ProgressBarPage::populate() hBox->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed ); hBox->setSpacing( 20 ); - { - auto bar = new ProgressBar( hBox ); - bar->setValue( 35 ); - } + QVector< QskProgressIndicator* > determinateIndicators; { auto bar = new ProgressBar( hBox ); @@ -77,6 +102,11 @@ void ProgressBarPage::populate() bar->setValue( 25 ); } + { + auto bar = new ProgressBar( hBox ); + determinateIndicators.append( bar ); + } + { auto bar = new ProgressBar( hBox ); bar->setIndeterminate( true ); @@ -106,8 +136,55 @@ void ProgressBarPage::populate() bar->setValue( 10 ); } + { + auto bar = new ProgressBar( vBox ); + determinateIndicators.append( bar ); + } + { auto bar = new ProgressBar( vBox ); bar->setIndeterminate( true ); } + + const auto sizes = { QskProgressRing::SmallSize, QskProgressRing::NormalSize, + QskProgressRing::LargeSize }; + + auto determinateRingsHBox = new QskLinearBox( Qt::Horizontal, vBox ); + + auto indeterminateRingsHBox = new QskLinearBox( Qt::Horizontal, vBox ); + + for( const auto size : sizes ) + { + for( const auto indeterminate : { true, false } ) + { + auto* ring = new QskProgressRing( determinateRingsHBox ); + ring->setSize( size ); + + QQuickItem* parentItem; + + if( indeterminate ) + { + parentItem = indeterminateRingsHBox; + ring->setIndeterminate( true ); + } + else + { + parentItem = determinateRingsHBox; + determinateIndicators.append( ring ); + } + + ring->setParent( parentItem ); + ring->setParentItem( parentItem ); + } + } + + connect( this, &QskQuickItem::windowChanged, this, [this, determinateIndicators]( QQuickWindow* window ) + { + if( window ) + { + m_determinateIndicatorsAnimator.reset( new DeterminateIndicatorsAnimator( determinateIndicators ) ); + m_determinateIndicatorsAnimator->setWindow( window ); + m_determinateIndicatorsAnimator->start(); + } + } ); } diff --git a/examples/gallery/progressbar/ProgressBarPage.h b/examples/gallery/progressbar/ProgressBarPage.h index 0d6fdec4..7f18c15a 100644 --- a/examples/gallery/progressbar/ProgressBarPage.h +++ b/examples/gallery/progressbar/ProgressBarPage.h @@ -7,6 +7,8 @@ #include "Page.h" +class QskAnimator; + class ProgressBarPage : public Page { public: @@ -14,4 +16,6 @@ class ProgressBarPage : public Page private: void populate(); + + std::unique_ptr< QskAnimator > m_determinateIndicatorsAnimator; }; diff --git a/examples/iotdashboard/Skin.cpp b/examples/iotdashboard/Skin.cpp index 56fb9135..f369c58f 100644 --- a/examples/iotdashboard/Skin.cpp +++ b/examples/iotdashboard/Skin.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include diff --git a/examples/iotdashboard/StoragePage.cpp b/examples/iotdashboard/StoragePage.cpp index da2ca929..35261843 100644 --- a/examples/iotdashboard/StoragePage.cpp +++ b/examples/iotdashboard/StoragePage.cpp @@ -5,9 +5,6 @@ #include "StoragePage.h" #include "Box.h" -#include "CircularProgressBar.h" -#include "Diagram.h" -#include "EnergyMeter.h" #include "StorageBar.h" #include "StorageMeter.h" #include @@ -17,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/qmlexport/QskQml.cpp b/qmlexport/QskQml.cpp index b2019d2d..d15be6a7 100644 --- a/qmlexport/QskQml.cpp +++ b/qmlexport/QskQml.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -220,6 +221,7 @@ void QskQml::registerTypes() registerObject< QskFocusIndicator >(); registerObject< QskSeparator >(); registerObject< QskProgressBar >(); + registerObject< QskProgressRing >(); registerObject< QskPushButton >(); registerObject< QskScrollView >(); registerObject< QskScrollArea >(); diff --git a/skins/fluent2/QskFluent2Skin.cpp b/skins/fluent2/QskFluent2Skin.cpp index 85227355..7261baaa 100644 --- a/skins/fluent2/QskFluent2Skin.cpp +++ b/skins/fluent2/QskFluent2Skin.cpp @@ -80,6 +80,7 @@ #include #include #include +#include #include #include #include @@ -212,6 +213,9 @@ namespace void setupProgressBarMetrics(); void setupProgressBarColors( QskAspect::Section, const QskFluent2Theme& ); + void setupProgressRingMetrics(); + void setupProgressRingColors( QskAspect::Section, const QskFluent2Theme& ); + void setupPushButtonMetrics(); void setupPushButtonColors( QskAspect::Section, const QskFluent2Theme& ); @@ -291,6 +295,7 @@ void Editor::setupMetrics() setupMenuMetrics(); setupPageIndicatorMetrics(); setupProgressBarMetrics(); + setupProgressRingMetrics(); setupPushButtonMetrics(); setupRadioBoxMetrics(); setupScrollViewMetrics(); @@ -327,6 +332,7 @@ void Editor::setupColors( QskAspect::Section section, const QskFluent2Theme& the setupMenuColors( section, theme ); setupPageIndicatorColors( section, theme ); setupProgressBarColors( section, theme ); + setupProgressRingColors( section, theme ); setupPushButtonColors( section, theme ); setupRadioBoxColors( section, theme ); setupScrollViewColors( section, theme ); @@ -675,7 +681,7 @@ void Editor::setupListViewColors( const auto text = Q::Text | section | state1 | state2; setGradient( cell, cellColor ); - + { /* We are using a section of the left border to display a @@ -839,8 +845,8 @@ void Editor::setupProgressBarMetrics() setMetric( Q::Groove | A::Size, 1 ); setBoxShape( Q::Groove, 100, Qt::RelativeSize ); - setMetric( Q::Bar | A::Size, 3 ); - setBoxShape( Q::Bar, 100, Qt::RelativeSize ); + setMetric( Q::Fill | A::Size, 3 ); + setBoxShape( Q::Fill, 100, Qt::RelativeSize ); } void Editor::setupProgressBarColors( @@ -851,7 +857,36 @@ void Editor::setupProgressBarColors( const auto& pal = theme.palette; setGradient( Q::Groove | section, pal.strokeColor.controlStrong.defaultColor ); - setGradient( Q::Bar | section, pal.fillColor.accent.defaultColor ); + setGradient( Q::Fill | section, pal.fillColor.accent.defaultColor ); +} + +void Editor::setupProgressRingMetrics() +{ + using Q = QskProgressRing; + using A = QskAspect; + + static constexpr QskAspect::Variation SmallSize = A::Small; + static constexpr QskAspect::Variation NormalSize = A::NoVariation; + static constexpr QskAspect::Variation LargeSize = A::Large; + + setStrutSize( Q::Fill | SmallSize, { 16, 16 } ); + setStrutSize( Q::Fill | NormalSize, { 32, 32 } ); + setStrutSize( Q::Fill | LargeSize, { 64, 64 } ); + + const auto startAngle = 90, spanAngle = -360; + setArcMetrics( Q::Fill | SmallSize, startAngle, spanAngle, 1.5 ); + setArcMetrics( Q::Fill | NormalSize, startAngle, spanAngle, 3 ); + setArcMetrics( Q::Fill | LargeSize, startAngle, spanAngle, 6 ); +} + +void Editor::setupProgressRingColors( + QskAspect::Section section, const QskFluent2Theme& theme ) +{ + using Q = QskProgressRing; + + const auto& pal = theme.palette; + + setGradient( Q::Fill | section, pal.fillColor.accent.defaultColor ); } void Editor::setupPushButtonMetrics() diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index 564b0521..f9537a55 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -94,6 +95,7 @@ namespace void setupPageIndicator(); void setupPopup(); void setupProgressBar(); + void setupProgressRing(); void setupRadioBox(); void setupPushButton(); void setupScrollView(); @@ -161,6 +163,7 @@ void Editor::setup() setupPageIndicator(); setupPopup(); setupProgressBar(); + setupProgressRing(); setupPushButton(); setupRadioBox(); setupScrollView(); @@ -431,9 +434,9 @@ void Editor::setupProgressBar() using A = QskAspect; using Q = QskProgressBar; - auto size = 5_dp; + auto size = 4_dp; - for ( auto subControl : { Q::Groove, Q::Bar } ) + for ( auto subControl : { Q::Groove, Q::Fill } ) { setMetric( subControl | A::Size, size ); setPadding( subControl, 0 ); @@ -443,12 +446,21 @@ void Editor::setupProgressBar() } setMetric( Q::Groove | A::Size, size ); - setGradient( Q::Groove, m_pal.primaryContainer ); + setGradient( Q::Groove, m_pal.surfaceContainerHighest ); setGradient( Q::Groove | Q::Disabled, m_pal.onSurface12 ); - setGradient( Q::Bar, m_pal.primary ); - setGradient( Q::Bar | Q::Disabled, m_pal.onSurface38 ); + setGradient( Q::Fill, m_pal.primary ); + setGradient( Q::Fill | Q::Disabled, m_pal.onSurface38 ); +} + +void Editor::setupProgressRing() +{ + using Q = QskProgressRing; + + setStrutSize( Q::Fill, { 48_dp, 48_dp } ); + setGradient( Q::Fill, m_pal.primary ); + setArcMetrics( Q::Fill, 90, -360, 4_dp ); } void Editor::setupRadioBox() @@ -538,7 +550,7 @@ void Editor::setupSegmentedBar() setGradient( Q::Segment | Q::Selected | Q::Hovered, flattenedColor( m_pal.onSurface, m_pal.secondaryContainer, m_pal.hoverOpacity ) ); - + setGradient( Q::Segment | Q::Selected | Q::Focused, flattenedColor( m_pal.onSurface, m_pal.secondaryContainer, m_pal.focusOpacity ) ); @@ -1302,6 +1314,8 @@ QskMaterial3Theme::QskMaterial3Theme( QskSkin::ColorScheme colorScheme, outline = m_palettes[ NeutralVariant ].toned( 50 ).rgb(); outlineVariant = m_palettes[ NeutralVariant ].toned( 80 ).rgb(); + surfaceContainerHighest = m_palettes[ NeutralVariant ].toned( 90 ).rgb(); + shadow = m_palettes[ Neutral ].toned( 0 ).rgb(); } else if ( colorScheme == QskSkin::DarkScheme ) @@ -1336,6 +1350,8 @@ QskMaterial3Theme::QskMaterial3Theme( QskSkin::ColorScheme colorScheme, outline = m_palettes[ NeutralVariant ].toned( 60 ).rgb(); outlineVariant = m_palettes[ NeutralVariant ].toned( 30 ).rgb(); + surfaceContainerHighest = m_palettes[ NeutralVariant ].toned( 22 ).rgb(); + shadow = m_palettes[ Neutral ].toned( 0 ).rgb(); } diff --git a/skins/material3/QskMaterial3Skin.h b/skins/material3/QskMaterial3Skin.h index ce36e9bd..2dbdcbc9 100644 --- a/skins/material3/QskMaterial3Skin.h +++ b/skins/material3/QskMaterial3Skin.h @@ -79,6 +79,8 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Theme QRgb outline; QRgb outlineVariant; + QRgb surfaceContainerHighest; + QRgb shadow; QskShadowMetrics elevation0; diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index 3e3bbff3..d116d9db 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -162,6 +163,7 @@ namespace void setupPageIndicator(); void setupPopup(); void setupProgressBar(); + void setupProgressRing(); void setupPushButton(); void setupRadioBox(); void setupScrollView(); @@ -287,6 +289,7 @@ void Editor::setup() setupPageIndicator(); setupPopup(); setupProgressBar(); + setupProgressRing(); setupPushButton(); setupRadioBox(); setupScrollView(); @@ -521,15 +524,35 @@ void Editor::setupProgressBar() using A = QskAspect; using Q = QskProgressBar; - for ( auto subControl : { Q::Groove, Q::Bar } ) + for ( auto subControl : { Q::Groove, Q::Fill } ) { setMetric( subControl | A::Size, 6 ); setPadding( subControl, 0 ); setBoxShape( subControl, 4 ); } - setGradient( Q::Groove, m_pal.darker200 ); - setGradient( Q::Bar, m_pal.highlighted ); + setGradient( Q::Groove, m_pal.lighter110 ); + setGradient( Q::Fill, m_pal.highlighted ); +} + +void Editor::setupProgressRing() +{ + using A = QskAspect; + using Q = QskProgressRing; + + for ( auto subControl : { Q::Groove, Q::Fill } ) + { + setMetric( subControl | A::Size, 6 ); + setPadding( subControl, 0 ); + setBoxShape( subControl, 4 ); + } + + setArcMetrics( Q::Groove, 90, -360, 6 ); + setGradient( Q::Groove, m_pal.lighter110 ); + + setStrutSize( Q::Fill, { 60, 60 } ); + setGradient( Q::Fill, m_pal.highlighted ); + setArcMetrics( Q::Fill, 90, -360, 6 ); } void Editor::setupFocusIndicator() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 07b1b761..f244a2bb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -196,10 +196,14 @@ list(APPEND HEADERS controls/QskPanGestureRecognizer.h controls/QskPopup.h controls/QskPopupSkinlet.h - controls/QskPushButton.h - controls/QskPushButtonSkinlet.h controls/QskProgressBar.h controls/QskProgressBarSkinlet.h + controls/QskProgressIndicator.h + controls/QskProgressIndicatorSkinlet.h + controls/QskProgressRing.h + controls/QskProgressRingSkinlet.h + controls/QskPushButton.h + controls/QskPushButtonSkinlet.h controls/QskQuick.h controls/QskQuickItem.h controls/QskRadioBox.h @@ -294,10 +298,14 @@ list(APPEND SOURCES controls/QskPanGestureRecognizer.cpp controls/QskPopup.cpp controls/QskPopupSkinlet.cpp - controls/QskPushButton.cpp - controls/QskPushButtonSkinlet.cpp controls/QskProgressBar.cpp controls/QskProgressBarSkinlet.cpp + controls/QskProgressIndicator.cpp + controls/QskProgressIndicatorSkinlet.cpp + controls/QskProgressRing.cpp + controls/QskProgressRingSkinlet.cpp + controls/QskPushButton.cpp + controls/QskPushButtonSkinlet.cpp controls/QskQuick.cpp controls/QskQuickItem.cpp controls/QskQuickItemPrivate.cpp diff --git a/src/controls/QskProgressBar.cpp b/src/controls/QskProgressBar.cpp index 7a496b2d..92a964ce 100644 --- a/src/controls/QskProgressBar.cpp +++ b/src/controls/QskProgressBar.cpp @@ -6,91 +6,30 @@ #include "QskProgressBar.h" #include "QskIntervalF.h" -#include "QskFunctions.h" -#include "QskAnimator.h" -#include "QskAspect.h" QSK_SUBCONTROL( QskProgressBar, Groove ) -QSK_SUBCONTROL( QskProgressBar, Bar ) - -namespace -{ - class PositionAnimator : public QskAnimator - { - public: - PositionAnimator( QskProgressBar* progressBar ) - : m_progressBar( progressBar ) - { - setAutoRepeat( true ); - setDuration( 1300 ); - - setWindow( progressBar->window() ); - } - - void advance( qreal value ) override - { - if ( m_progressBar->setPositionHint( QskProgressBar::Bar, value ) ) - m_progressBar->update(); - } - - private: - QskProgressBar* m_progressBar; - }; -} +QSK_SUBCONTROL( QskProgressBar, Fill ) class QskProgressBar::PrivateData { public: - void updateIndeterminateAnimator( QskProgressBar* progressBar ) - { - if ( !isIndeterminate ) - { - delete animator; - animator = nullptr; - - return; - } - - if ( progressBar->window() && progressBar->isVisible() ) - { - if ( animator == nullptr ) - animator = new PositionAnimator( progressBar ); - - animator->start(); - } - else - { - if ( animator ) - animator->stop(); - } - } - - PositionAnimator* animator = nullptr; - - qreal value = 0.0; - qreal origin = 0.0; - - bool hasOrigin = false; - bool isIndeterminate = false; - Qt::Orientation orientation; }; QskProgressBar::QskProgressBar( Qt::Orientation orientation, qreal min, qreal max, QQuickItem* parent ) - : QskBoundedControl( min, max, parent ) + : Inherited( min, max, parent ) , m_data( new PrivateData ) { m_data->orientation = orientation; - m_data->value = minimum(); if ( orientation == Qt::Horizontal ) initSizePolicy( QskSizePolicy::MinimumExpanding, QskSizePolicy::Fixed ); else initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::MinimumExpanding ); - connect( this, &QskBoundedControl::boundariesChanged, - this, &QskProgressBar::adjustValue ); + setSubcontrolProxy( Inherited::Groove, Groove ); + setSubcontrolProxy( Inherited::Fill, Fill ); } QskProgressBar::QskProgressBar( Qt::Orientation orientation, QQuickItem* parent ) @@ -115,7 +54,12 @@ QskProgressBar::QskProgressBar( QQuickItem* parent ) QskProgressBar::~QskProgressBar() { - delete m_data->animator; +} + +QskAspect::Variation QskProgressBar::effectiveVariation() const +{ + // so you can define different hints depending on the orientation + return static_cast< QskAspect::Variation >( m_data->orientation ); } Qt::Orientation QskProgressBar::orientation() const @@ -137,163 +81,4 @@ void QskProgressBar::setOrientation( Qt::Orientation orientation ) } } -bool QskProgressBar::isIndeterminate() const -{ - return m_data->isIndeterminate; -} - -void QskProgressBar::setIndeterminate( bool on ) -{ - if ( on == m_data->isIndeterminate ) - return; - - m_data->isIndeterminate = on; - m_data->updateIndeterminateAnimator( this ); - - update(); - Q_EMIT indeterminateChanged( on ); -} - -QskAspect::Variation QskProgressBar::effectiveVariation() const -{ - // so you can define different hints depending on the orientation - return static_cast< QskAspect::Variation >( m_data->orientation ); -} - -void QskProgressBar::setBarGradient( const QskGradient& gradient ) -{ - setGradientHint( Bar, gradient ); -} - -void QskProgressBar::resetBarGradient() -{ - resetColor( Bar ); -} - -QskGradient QskProgressBar::barGradient() const -{ - return gradientHint( QskProgressBar::Bar ); -} - -void QskProgressBar::setExtent( qreal extent ) -{ - if ( extent < 0.0 ) - extent = 0.0; - - if ( setMetric( Groove | QskAspect::Size, extent ) ) - Q_EMIT extentChanged( extent ); -} - -void QskProgressBar::resetExtent() -{ - if ( resetMetric( Groove | QskAspect::Size ) ) - Q_EMIT extentChanged( extent() ); -} - -qreal QskProgressBar::extent() const -{ - auto grooveSize = metric( Groove | QskAspect::Size ); - auto barSize = metric( Bar | QskAspect::Size ); - return qMax( grooveSize, barSize ); -} - -void QskProgressBar::setOrigin( qreal origin ) -{ - if ( isComponentComplete() ) - origin = boundedValue( origin ); - - if( !m_data->hasOrigin || !qskFuzzyCompare( m_data->origin, origin ) ) - { - m_data->hasOrigin = true; - m_data->origin = origin; - - update(); - Q_EMIT originChanged( origin ); - } -} - -void QskProgressBar::resetOrigin() -{ - if ( m_data->hasOrigin ) - { - m_data->hasOrigin = false; - - update(); - Q_EMIT originChanged( origin() ); - } -} - -qreal QskProgressBar::origin() const -{ - if ( m_data->hasOrigin ) - { - return boundedValue( m_data->origin ); - } - - return minimum(); -} - -void QskProgressBar::setValue( qreal value ) -{ - if ( isComponentComplete() ) - value = boundedValue( value ); - - setValueInternal( value ); -} - -qreal QskProgressBar::value() const -{ - return m_data->value; -} - -void QskProgressBar::setValueAsRatio( qreal ratio ) -{ - ratio = qBound( 0.0, ratio, 1.0 ); - setValue( minimum() + ratio * boundaryLength() ); -} - -qreal QskProgressBar::valueAsRatio() const -{ - return valueAsRatio( m_data->value ); -} - -void QskProgressBar::componentComplete() -{ - Inherited::componentComplete(); - adjustValue(); -} - -void QskProgressBar::adjustValue() -{ - if ( isComponentComplete() ) - setValueInternal( boundedValue( m_data->value ) ); -} - -void QskProgressBar::setValueInternal( qreal value ) -{ - if ( !qskFuzzyCompare( value, m_data->value ) ) - { - m_data->value = value; - Q_EMIT valueChanged( value ); - - update(); - } -} - -void QskProgressBar::itemChange( QQuickItem::ItemChange change, - const QQuickItem::ItemChangeData& value ) -{ - switch( static_cast< int >( change ) ) - { - case QQuickItem::ItemVisibleHasChanged: - case QQuickItem::ItemSceneChange: - { - m_data->updateIndeterminateAnimator( this ); - break; - } - } - - Inherited::itemChange( change, value ); -} - #include "moc_QskProgressBar.cpp" diff --git a/src/controls/QskProgressBar.h b/src/controls/QskProgressBar.h index f41dc387..f1f1b613 100644 --- a/src/controls/QskProgressBar.h +++ b/src/controls/QskProgressBar.h @@ -6,34 +6,19 @@ #ifndef QSK_PROGRESS_BAR_H #define QSK_PROGRESS_BAR_H -#include "QskBoundedControl.h" +#include "QskProgressIndicator.h" -class QskIntervalF; - -class QSK_EXPORT QskProgressBar : public QskBoundedControl +class QSK_EXPORT QskProgressBar : public QskProgressIndicator { Q_OBJECT Q_PROPERTY( Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged ) - Q_PROPERTY( qreal extent READ extent - WRITE setExtent RESET resetExtent NOTIFY extentChanged ) - - Q_PROPERTY( bool indeterminate READ isIndeterminate - WRITE setIndeterminate NOTIFY indeterminateChanged ) - - Q_PROPERTY( qreal origin READ origin - WRITE setOrigin RESET resetOrigin NOTIFY originChanged ) - - Q_PROPERTY( qreal value READ value WRITE setValue NOTIFY valueChanged ) - Q_PROPERTY( qreal valueAsRatio READ valueAsRatio - WRITE setValueAsRatio NOTIFY valueChanged ) - - using Inherited = QskBoundedControl; + using Inherited = QskProgressIndicator; public: - QSK_SUBCONTROLS( Groove, Bar ) + QSK_SUBCONTROLS( Groove, Fill ) QskProgressBar( Qt::Orientation, QQuickItem* parent = nullptr ); QskProgressBar( Qt::Orientation, qreal min, qreal max, QQuickItem* parent = nullptr ); @@ -43,50 +28,15 @@ class QSK_EXPORT QskProgressBar : public QskBoundedControl ~QskProgressBar() override; + QskAspect::Variation effectiveVariation() const override; + Qt::Orientation orientation() const; void setOrientation( Qt::Orientation orientation ); - bool isIndeterminate() const; - void setIndeterminate( bool on = true ); - - QskAspect::Variation effectiveVariation() const override; - - void setBarGradient( const QskGradient& ); - void resetBarGradient(); - QskGradient barGradient() const; - - void setExtent( qreal ); - void resetExtent(); - qreal extent() const; - - void resetOrigin(); - qreal origin() const; - - qreal value() const; - qreal valueAsRatio() const; // [0.0, 1.0] - using QskBoundedControl::valueAsRatio; - - public Q_SLOTS: - void setValue( qreal ); - void setValueAsRatio( qreal ); - void setOrigin( qreal ); - Q_SIGNALS: void orientationChanged( Qt::Orientation ); - void extentChanged( qreal ); - void indeterminateChanged( bool ); - void valueChanged( qreal ); - void originChanged( qreal ); - - protected: - void componentComplete() override; - void itemChange( ItemChange, const ItemChangeData& ) override; private: - void setValueInternal( qreal value ); - void adjustBoundaries( bool increasing ); - void adjustValue(); - class PrivateData; std::unique_ptr< PrivateData > m_data; }; diff --git a/src/controls/QskProgressBarSkinlet.cpp b/src/controls/QskProgressBarSkinlet.cpp index 91de022a..92be1c09 100644 --- a/src/controls/QskProgressBarSkinlet.cpp +++ b/src/controls/QskProgressBarSkinlet.cpp @@ -7,51 +7,54 @@ #include "QskProgressBar.h" #include "QskIntervalF.h" #include "QskBoxBorderMetrics.h" -#include "QskGradientDirection.h" #include #include -static inline QskIntervalF qskBarInterval( const QskProgressBar* bar ) +using Q = QskProgressBar; + +namespace { - qreal pos1, pos2; - - if ( bar->isIndeterminate() ) + QskIntervalF qskFillInterval( const Q* bar ) { - const auto pos = bar->positionHint( QskProgressBar::Bar ); + qreal pos1, pos2; - static const QEasingCurve curve( QEasingCurve::InOutCubic ); - - const qreal off = 0.15; - - pos1 = curve.valueForProgress( qMax( pos - off, 0.0 ) ); - pos2 = curve.valueForProgress( qMin( pos + off, 1.0 ) ); - } - else - { - pos1 = bar->valueAsRatio( bar->origin() ); - pos2 = bar->valueAsRatio( bar->value() ); - } - - if( bar->orientation() == Qt::Horizontal ) - { - if ( bar->layoutMirroring() ) + if ( bar->isIndeterminate() ) { - pos1 = 1.0 - pos1; - pos2 = 1.0 - pos2; + const auto pos = bar->positionHint( QskProgressIndicator::Fill ); + + static const QEasingCurve curve( QEasingCurve::InOutCubic ); + + const qreal off = 0.15; + + pos1 = curve.valueForProgress( qMax( pos - off, 0.0 ) ); + pos2 = curve.valueForProgress( qMin( pos + off, 1.0 ) ); } + else + { + pos1 = bar->valueAsRatio( bar->origin() ); + pos2 = bar->valueAsRatio( bar->value() ); + } + + 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 ); } - - if ( pos1 > pos2 ) - std::swap( pos1, pos2 ); - - return QskIntervalF( pos1, pos2 ); } QskProgressBarSkinlet::QskProgressBarSkinlet( QskSkin* skin ) - : QskSkinlet( skin ) + : Inherited( skin ) { - setNodeRoles( { GrooveRole, BarRole } ); } QskProgressBarSkinlet::~QskProgressBarSkinlet() @@ -62,7 +65,6 @@ QRectF QskProgressBarSkinlet::subControlRect( const QskSkinnable* skinnable, const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const { - using Q = QskProgressBar; const auto bar = static_cast< const Q* >( skinnable ); if( subControl == Q::Groove ) @@ -84,7 +86,7 @@ QRectF QskProgressBarSkinlet::subControlRect( return rect; } - if( subControl == Q::Bar ) + if( subControl == Q::Fill ) { return barRect( bar ); } @@ -92,30 +94,18 @@ QRectF QskProgressBarSkinlet::subControlRect( return Inherited::subControlRect( skinnable, contentsRect, subControl ); } -QSGNode* QskProgressBarSkinlet::updateSubNode( - const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +QSGNode* QskProgressBarSkinlet::updateGrooveNode( + const QskProgressIndicator* indicator, QSGNode* node ) const { - switch( nodeRole ) - { - case GrooveRole: - { - return updateBoxNode( skinnable, node, QskProgressBar::Groove ); - } - - case BarRole: - { - const auto bar = static_cast< const QskProgressBar* >( skinnable ); - return updateBarNode( bar, node ); - } - } - - return Inherited::updateSubNode( skinnable, nodeRole, node ); + return updateBoxNode( indicator, node, Q::Groove ); } -QSGNode* QskProgressBarSkinlet::updateBarNode( - const QskProgressBar* bar, QSGNode* node ) const +QSGNode* QskProgressBarSkinlet::updateFillNode( + const QskProgressIndicator* indicator, QSGNode* node ) const { - const auto subControl = QskProgressBar::Bar; + const auto bar = static_cast< const Q* >( indicator ); + + const auto subControl = Q::Fill; const auto rect = bar->subControlRect( subControl ); if ( rect.isEmpty() ) @@ -139,26 +129,27 @@ QSGNode* QskProgressBarSkinlet::updateBarNode( not supporting this yet. TODO ... */ - const auto intv = qskBarInterval( bar ); + const auto intv = qskFillInterval( bar ); const auto stops = qskExtractedGradientStops( gradient.stops(), intv.lowerBound(), intv.upperBound() ); gradient.setStops( stops ); - gradient.setLinearDirection( bar->orientation() ); + + gradient.setLinearDirection( static_cast< Qt::Orientation >( bar->orientation() ) ); + if ( bar->orientation() == Qt::Vertical || bar->layoutMirroring() ) gradient.reverse(); } - return updateBoxNode( bar, node, rect, gradient, subControl ); + return updateBoxNode( indicator, node, rect, gradient, subControl ); } -QRectF QskProgressBarSkinlet::barRect( const QskProgressBar* bar ) const +QRectF QskProgressBarSkinlet::barRect( const Q* bar ) const { - using Q = QskProgressBar; const auto subControl = Q::Groove; - const auto barSize = bar->metric( Q::Bar | QskAspect::Size ); + const auto barSize = bar->metric( Q::Fill | QskAspect::Size ); auto rect = bar->subControlRect( subControl ); if ( bar->orientation() == Qt::Horizontal ) @@ -179,7 +170,7 @@ QRectF QskProgressBarSkinlet::barRect( const QskProgressBar* bar ) const rect = rect.marginsRemoved( m ); - const auto intv = qskBarInterval( bar ); + const auto intv = qskFillInterval( bar ); if( bar->orientation() == Qt::Horizontal ) { @@ -205,7 +196,7 @@ QSizeF QskProgressBarSkinlet::sizeHint( const QskSkinnable* skinnable, if ( which != Qt::PreferredSize ) return QSizeF(); - const auto bar = static_cast< const QskProgressBar* >( skinnable ); + const auto bar = static_cast< const Q* >( skinnable ); const auto extent = bar->extent(); diff --git a/src/controls/QskProgressBarSkinlet.h b/src/controls/QskProgressBarSkinlet.h index f6af6114..a3f36cd1 100644 --- a/src/controls/QskProgressBarSkinlet.h +++ b/src/controls/QskProgressBarSkinlet.h @@ -6,25 +6,17 @@ #ifndef QSK_PROGRESS_BAR_SKINLET_H #define QSK_PROGRESS_BAR_SKINLET_H -#include "QskSkinlet.h" +#include "QskProgressIndicatorSkinlet.h" class QskProgressBar; -class QSK_EXPORT QskProgressBarSkinlet : public QskSkinlet +class QSK_EXPORT QskProgressBarSkinlet : public QskProgressIndicatorSkinlet { Q_GADGET - using Inherited = QskSkinlet; + using Inherited = QskProgressIndicatorSkinlet; public: - enum NodeRole - { - GrooveRole, - BarRole, - - RoleCount - }; - Q_INVOKABLE QskProgressBarSkinlet( QskSkin* = nullptr ); ~QskProgressBarSkinlet() override; @@ -35,11 +27,10 @@ class QSK_EXPORT QskProgressBarSkinlet : public QskSkinlet Qt::SizeHint, const QSizeF& ) 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; private: - QSGNode* updateBarNode( const QskProgressBar*, QSGNode* ) const; QRectF barRect( const QskProgressBar* ) const; }; diff --git a/src/controls/QskProgressIndicator.cpp b/src/controls/QskProgressIndicator.cpp new file mode 100644 index 00000000..9cf220e4 --- /dev/null +++ b/src/controls/QskProgressIndicator.cpp @@ -0,0 +1,255 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskProgressIndicator.h" + +#include "QskIntervalF.h" +#include "QskFunctions.h" +#include "QskAnimator.h" +#include "QskAspect.h" + +QSK_SUBCONTROL( QskProgressIndicator, Groove ) +QSK_SUBCONTROL( QskProgressIndicator, Fill ) + +namespace +{ + class PositionAnimator : public QskAnimator + { + public: + PositionAnimator( QskProgressIndicator* indicator ) + : m_indicator( indicator ) + { + setAutoRepeat( true ); + setDuration( 1300 ); + + setWindow( indicator->window() ); + } + + void advance( qreal value ) override + { + if ( m_indicator->setPositionHint( QskProgressIndicator::Fill, value ) ) + m_indicator->update(); + } + + private: + QskProgressIndicator* m_indicator; + }; +} + +class QskProgressIndicator::PrivateData +{ + public: + void updateIndeterminateAnimator( QskProgressIndicator* indicator ) + { + if ( !isIndeterminate ) + { + delete animator; + animator = nullptr; + + return; + } + + if ( indicator->window() && indicator->isVisible() ) + { + if ( animator == nullptr ) + animator = new PositionAnimator( indicator ); + + animator->start(); + } + else + { + if ( animator ) + animator->stop(); + } + } + + PositionAnimator* animator = nullptr; + + qreal value = 0.0; + qreal origin = 0.0; + + bool hasOrigin = false; + bool isIndeterminate = false; +}; + +QskProgressIndicator::QskProgressIndicator( qreal min, qreal max, QQuickItem* parent ) + : QskBoundedControl( min, max, parent ) + , m_data( new PrivateData ) +{ + m_data->value = minimum(); + + connect( this, &QskBoundedControl::boundariesChanged, + this, &QskProgressIndicator::adjustValue ); +} + +QskProgressIndicator::QskProgressIndicator( QQuickItem* parent ) + : QskProgressIndicator( 0.0, 100.0, parent ) +{ +} + +QskProgressIndicator::QskProgressIndicator( const QskIntervalF& boundaries, QQuickItem* parent ) + : QskProgressIndicator( boundaries.lowerBound(), boundaries.upperBound(), parent ) +{ +} + +QskProgressIndicator::~QskProgressIndicator() +{ + delete m_data->animator; +} + +bool QskProgressIndicator::isIndeterminate() const +{ + return m_data->isIndeterminate; +} + +void QskProgressIndicator::setIndeterminate( bool on ) +{ + if ( on == m_data->isIndeterminate ) + return; + + m_data->isIndeterminate = on; + m_data->updateIndeterminateAnimator( this ); + + update(); + Q_EMIT indeterminateChanged( on ); +} + +void QskProgressIndicator::setFillGradient( const QskGradient& gradient ) +{ + setGradientHint( Fill, gradient ); +} + +void QskProgressIndicator::resetFillGradient() +{ + resetColor( Fill ); +} + +QskGradient QskProgressIndicator::fillGradient() const +{ + return gradientHint( Fill ); +} + +void QskProgressIndicator::setExtent( qreal extent ) +{ + if ( extent < 0.0 ) + extent = 0.0; + + if ( setMetric( Groove | QskAspect::Size, extent ) ) + Q_EMIT extentChanged( extent ); +} + +void QskProgressIndicator::resetExtent() +{ + if ( resetMetric( Groove | QskAspect::Size ) ) + Q_EMIT extentChanged( extent() ); +} + +qreal QskProgressIndicator::extent() const +{ + auto grooveSize = metric( Groove | QskAspect::Size ); + auto fillSize = metric( Fill | QskAspect::Size ); + return qMax( grooveSize, fillSize ); +} + +void QskProgressIndicator::setOrigin( qreal origin ) +{ + if ( isComponentComplete() ) + origin = boundedValue( origin ); + + if( !m_data->hasOrigin || !qskFuzzyCompare( m_data->origin, origin ) ) + { + m_data->hasOrigin = true; + m_data->origin = origin; + + update(); + Q_EMIT originChanged( origin ); + } +} + +void QskProgressIndicator::resetOrigin() +{ + if ( m_data->hasOrigin ) + { + m_data->hasOrigin = false; + + update(); + Q_EMIT originChanged( origin() ); + } +} + +qreal QskProgressIndicator::origin() const +{ + if ( m_data->hasOrigin ) + { + return boundedValue( m_data->origin ); + } + + return minimum(); +} + +void QskProgressIndicator::setValue( qreal value ) +{ + if ( isComponentComplete() ) + value = boundedValue( value ); + + setValueInternal( value ); +} + +qreal QskProgressIndicator::value() const +{ + return m_data->value; +} + +void QskProgressIndicator::setValueAsRatio( qreal ratio ) +{ + ratio = qBound( 0.0, ratio, 1.0 ); + setValue( minimum() + ratio * boundaryLength() ); +} + +qreal QskProgressIndicator::valueAsRatio() const +{ + return valueAsRatio( m_data->value ); +} + +void QskProgressIndicator::componentComplete() +{ + Inherited::componentComplete(); + adjustValue(); +} + +void QskProgressIndicator::adjustValue() +{ + if ( isComponentComplete() ) + setValueInternal( boundedValue( m_data->value ) ); +} + +void QskProgressIndicator::setValueInternal( qreal value ) +{ + if ( !qskFuzzyCompare( value, m_data->value ) ) + { + m_data->value = value; + Q_EMIT valueChanged( value ); + + update(); + } +} + +void QskProgressIndicator::itemChange( QQuickItem::ItemChange change, + const QQuickItem::ItemChangeData& value ) +{ + switch( static_cast< int >( change ) ) + { + case QQuickItem::ItemVisibleHasChanged: + case QQuickItem::ItemSceneChange: + { + m_data->updateIndeterminateAnimator( this ); + break; + } + } + + Inherited::itemChange( change, value ); +} + +#include "moc_QskProgressIndicator.cpp" diff --git a/src/controls/QskProgressIndicator.h b/src/controls/QskProgressIndicator.h new file mode 100644 index 00000000..77d49002 --- /dev/null +++ b/src/controls/QskProgressIndicator.h @@ -0,0 +1,83 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_PROGRESS_INDICATOR_H +#define QSK_PROGRESS_INDICATOR_H + +#include "QskBoundedControl.h" + +class QskIntervalF; + +class QSK_EXPORT QskProgressIndicator : public QskBoundedControl +{ + Q_OBJECT + + Q_PROPERTY( qreal extent READ extent + WRITE setExtent RESET resetExtent NOTIFY extentChanged ) + + Q_PROPERTY( bool indeterminate READ isIndeterminate + WRITE setIndeterminate NOTIFY indeterminateChanged ) + + Q_PROPERTY( qreal origin READ origin + WRITE setOrigin RESET resetOrigin NOTIFY originChanged ) + + Q_PROPERTY( qreal value READ value WRITE setValue NOTIFY valueChanged ) + Q_PROPERTY( qreal valueAsRatio READ valueAsRatio + WRITE setValueAsRatio NOTIFY valueChanged ) + + using Inherited = QskBoundedControl; + + public: + QSK_SUBCONTROLS( Groove, Fill ) + + QskProgressIndicator( QQuickItem* parent = nullptr ); + QskProgressIndicator( qreal min, qreal max, QQuickItem* parent = nullptr ); + QskProgressIndicator( const QskIntervalF&, QQuickItem* parent = nullptr ); + + ~QskProgressIndicator() override; + + bool isIndeterminate() const; + void setIndeterminate( bool on = true ); + + void setFillGradient( const QskGradient& ); + void resetFillGradient(); + QskGradient fillGradient() const; + + void setExtent( qreal ); + void resetExtent(); + qreal extent() const; + + void resetOrigin(); + qreal origin() const; + + qreal value() const; + qreal valueAsRatio() const; // [0.0, 1.0] + using QskBoundedControl::valueAsRatio; + + public Q_SLOTS: + void setValue( qreal ); + void setValueAsRatio( qreal ); + void setOrigin( qreal ); + + Q_SIGNALS: + void extentChanged( qreal ); + void indeterminateChanged( bool ); + void valueChanged( qreal ); + void originChanged( qreal ); + + protected: + void componentComplete() override; + void itemChange( ItemChange, const ItemChangeData& ) override; + + private: + void setValueInternal( qreal value ); + void adjustBoundaries( bool increasing ); + void adjustValue(); + + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +#endif diff --git a/src/controls/QskProgressIndicatorSkinlet.cpp b/src/controls/QskProgressIndicatorSkinlet.cpp new file mode 100644 index 00000000..f107409c --- /dev/null +++ b/src/controls/QskProgressIndicatorSkinlet.cpp @@ -0,0 +1,40 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskProgressIndicatorSkinlet.h" +#include "QskProgressIndicator.h" + +QskProgressIndicatorSkinlet::QskProgressIndicatorSkinlet( QskSkin* skin ) + : Inherited( skin ) +{ + setNodeRoles( { GrooveRole, FillRole } ); +} + +QskProgressIndicatorSkinlet::~QskProgressIndicatorSkinlet() +{ +} + +QSGNode* QskProgressIndicatorSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + const auto indicator = static_cast< const QskProgressIndicator* >( skinnable ); + + switch( nodeRole ) + { + case GrooveRole: + { + return updateGrooveNode( indicator, node ); + } + + case FillRole: + { + return updateFillNode( indicator, node ); + } + } + + return Inherited::updateSubNode( skinnable, nodeRole, node ); +} + +#include "moc_QskProgressIndicatorSkinlet.cpp" diff --git a/src/controls/QskProgressIndicatorSkinlet.h b/src/controls/QskProgressIndicatorSkinlet.h new file mode 100644 index 00000000..effc2b13 --- /dev/null +++ b/src/controls/QskProgressIndicatorSkinlet.h @@ -0,0 +1,37 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_PROGRESS_INDICATOR_SKINLET_H +#define QSK_PROGRESS_INDICATOR_SKINLET_H + +#include "QskSkinlet.h" + +class QskProgressIndicator; + +class QSK_EXPORT QskProgressIndicatorSkinlet : public QskSkinlet +{ + using Inherited = QskSkinlet; + + public: + enum NodeRole + { + GrooveRole, + FillRole, + + RoleCount + }; + + QskProgressIndicatorSkinlet( QskSkin* = nullptr ); + ~QskProgressIndicatorSkinlet() override; + + protected: + QSGNode* updateSubNode( const QskSkinnable*, + quint8 nodeRole, QSGNode* ) const override; + + virtual QSGNode* updateGrooveNode( const QskProgressIndicator*, QSGNode* ) const = 0; + virtual QSGNode* updateFillNode( const QskProgressIndicator*, QSGNode* ) const = 0; +}; + +#endif diff --git a/src/controls/QskProgressRing.cpp b/src/controls/QskProgressRing.cpp new file mode 100644 index 00000000..9734ccab --- /dev/null +++ b/src/controls/QskProgressRing.cpp @@ -0,0 +1,76 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2023 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskProgressRing.h" + +#include "QskIntervalF.h" + +QSK_SUBCONTROL( QskProgressRing, Groove ) +QSK_SUBCONTROL( QskProgressRing, Fill ) + +class QskProgressRing::PrivateData +{ + public: + int size : 2; +}; + +QskProgressRing::QskProgressRing( qreal min, qreal max, QQuickItem* parent ) + : Inherited( min, max, parent ) + , m_data( new PrivateData ) +{ + m_data->size = NormalSize; + + setSubcontrolProxy( Inherited::Groove, Groove ); + setSubcontrolProxy( Inherited::Fill, Fill ); +} + +QskProgressRing::QskProgressRing( QQuickItem* parent ) + : QskProgressRing( 0.0, 100.0, parent ) +{ +} + +QskProgressRing::QskProgressRing( const QskIntervalF& boundaries, QQuickItem* parent ) + : QskProgressRing( boundaries.lowerBound(), boundaries.upperBound(), parent ) +{ +} + +QskProgressRing::~QskProgressRing() +{ +} + +QskAspect::Variation QskProgressRing::effectiveVariation() const +{ + switch( size() ) + { + case SmallSize: + return QskAspect::Small; + + case LargeSize: + return QskAspect::Large; + + default: + return QskAspect::NoVariation; + } +} + +void QskProgressRing::setSize( Size size ) +{ + if ( size != m_data->size ) + { + m_data->size = size; + + resetImplicitSize(); + update(); + + Q_EMIT sizeChanged( size ); + } +} + +QskProgressRing::Size QskProgressRing::size() const +{ + return static_cast< Size >( m_data->size ); +} + +#include "moc_QskProgressRing.cpp" diff --git a/src/controls/QskProgressRing.h b/src/controls/QskProgressRing.h new file mode 100644 index 00000000..e4ce68e2 --- /dev/null +++ b/src/controls/QskProgressRing.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2023 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_PROGRESS_RING_H +#define QSK_PROGRESS_RING_H + +#include "QskProgressIndicator.h" + +class QSK_EXPORT QskProgressRing : public QskProgressIndicator +{ + Q_OBJECT + + Q_PROPERTY( Size size READ size + WRITE setSize NOTIFY sizeChanged ) + + using Inherited = QskProgressIndicator; + + public: + QSK_SUBCONTROLS( Groove, Fill ) + + enum Size + { + SmallSize = -1, + NormalSize = 0, + LargeSize = 1 + }; + Q_ENUM( Size ) + + QskProgressRing( QQuickItem* parent = nullptr ); + QskProgressRing( qreal min, qreal max, QQuickItem* parent = nullptr ); + QskProgressRing( const QskIntervalF&, QQuickItem* parent = nullptr ); + + ~QskProgressRing() override; + + QskAspect::Variation effectiveVariation() const override; + + void setSize( Size ); + Size size() const; + + Q_SIGNALS: + void sizeChanged( Size ); + + private: + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +#endif diff --git a/src/controls/QskProgressRingSkinlet.cpp b/src/controls/QskProgressRingSkinlet.cpp new file mode 100644 index 00000000..fa5c7ca5 --- /dev/null +++ b/src/controls/QskProgressRingSkinlet.cpp @@ -0,0 +1,125 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2023 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskProgressRingSkinlet.h" +#include "QskArcMetrics.h" +#include "QskProgressRing.h" +#include "QskIntervalF.h" + +using Q = QskProgressRing; + +namespace +{ + QskIntervalF qskFillInterval( const Q* ring ) + { + qreal pos1, pos2; + + if ( ring->isIndeterminate() ) + { + const auto pos = ring->positionHint( QskProgressIndicator::Fill ); + + pos1 = pos2 = pos; + } + else + { + pos1 = ring->valueAsRatio( ring->origin() ); + pos2 = ring->valueAsRatio( ring->value() ); + } + + if ( pos1 > pos2 ) + std::swap( pos1, pos2 ); + + return QskIntervalF( pos1, pos2 ); + } +} + +QskProgressRingSkinlet::QskProgressRingSkinlet( QskSkin* skin ) + : Inherited( skin ) +{ +} + +QskProgressRingSkinlet::~QskProgressRingSkinlet() +{ +} + +QRectF QskProgressRingSkinlet::subControlRect( + const QskSkinnable* skinnable, const QRectF& contentsRect, + QskAspect::Subcontrol subControl ) const +{ + const auto ring = static_cast< const Q* >( skinnable ); + + if( subControl == Q::Groove || subControl == Q::Fill ) + { + auto rect = contentsRect; + const auto size = ring->strutSizeHint( Q::Fill ); + + if( ring->layoutMirroring() ) + { + rect.setLeft( rect.right() - size.width() ); + } + else + { + rect.setWidth( size.width() ); + } + + rect.setTop( rect.top() + 0.5 * ( rect.height() - size.height() ) ); + rect.setHeight( size.height() ); + + return rect; + } + + return Inherited::subControlRect( skinnable, contentsRect, subControl ); +} + +QSGNode* QskProgressRingSkinlet::updateGrooveNode( + const QskProgressIndicator* indicator, QSGNode* node ) const +{ + return updateArcNode( indicator, node, Q::Groove ); +} + +QSGNode* QskProgressRingSkinlet::updateFillNode( + const QskProgressIndicator* indicator, QSGNode* node ) const +{ + const auto ring = static_cast< const Q* >( indicator ); + + const auto subControl = Q::Fill; + + const auto rect = ring->subControlRect( subControl ); + if ( rect.isEmpty() ) + return nullptr; + + auto gradient = ring->gradientHint( subControl ); + if ( !gradient.isVisible() ) + return nullptr; + + if ( ( gradient.type() == QskGradient::Stops ) && !gradient.isMonochrome() ) + { + const auto center = rect.center(); + const auto arcMetrics = ring->arcMetricsHint( Q::Fill ); + gradient.setConicDirection( center.x(), center.y(), arcMetrics.startAngle(), arcMetrics.spanAngle() ); + } + + const auto interval = qskFillInterval( ring ); + const auto arcMetrics = ring->arcMetricsHint( subControl ); + + const auto startAngle = arcMetrics.startAngle() + interval.lowerBound() * arcMetrics.spanAngle(); + const auto spanAngle = interval.upperBound() * arcMetrics.spanAngle(); + + return updateArcNode( ring, node, rect, gradient, startAngle, spanAngle, subControl ); +} + +QSizeF QskProgressRingSkinlet::sizeHint( const QskSkinnable* skinnable, + Qt::SizeHint which, const QSizeF& ) const +{ + if ( which != Qt::PreferredSize ) + return QSizeF(); + + const auto ring = static_cast< const Q* >( skinnable ); + + const auto r = ring->strutSizeHint( Q::Fill ); + return r; +} + +#include "moc_QskProgressRingSkinlet.cpp" diff --git a/src/controls/QskProgressRingSkinlet.h b/src/controls/QskProgressRingSkinlet.h new file mode 100644 index 00000000..009640ee --- /dev/null +++ b/src/controls/QskProgressRingSkinlet.h @@ -0,0 +1,34 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2023 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_PROGRESS_RING_SKINLET_H +#define QSK_PROGRESS_RING_SKINLET_H + +#include "QskProgressIndicatorSkinlet.h" + +class QskProgressRing; + +class QSK_EXPORT QskProgressRingSkinlet : public QskProgressIndicatorSkinlet +{ + Q_GADGET + + using Inherited = QskProgressIndicatorSkinlet; + + public: + Q_INVOKABLE QskProgressRingSkinlet( QskSkin* = nullptr ); + ~QskProgressRingSkinlet() override; + + QRectF subControlRect( const QskSkinnable*, + const QRectF&, QskAspect::Subcontrol ) const override; + + QSizeF sizeHint( const QskSkinnable*, + Qt::SizeHint, const QSizeF& ) const override; + + protected: + QSGNode* updateGrooveNode( const QskProgressIndicator*, QSGNode* ) const override; + QSGNode* updateFillNode( const QskProgressIndicator*, QSGNode* ) const override; +}; + +#endif diff --git a/src/controls/QskSkin.cpp b/src/controls/QskSkin.cpp index c3f66a71..a24a212f 100644 --- a/src/controls/QskSkin.cpp +++ b/src/controls/QskSkin.cpp @@ -53,6 +53,9 @@ #include "QskProgressBar.h" #include "QskProgressBarSkinlet.h" +#include "QskProgressRing.h" +#include "QskProgressRingSkinlet.h" + #include "QskRadioBox.h" #include "QskRadioBoxSkinlet.h" @@ -178,6 +181,7 @@ QskSkin::QskSkin( QObject* parent ) declareSkinlet< QskTextLabel, QskTextLabelSkinlet >(); declareSkinlet< QskTextInput, QskTextInputSkinlet >(); declareSkinlet< QskProgressBar, QskProgressBarSkinlet >(); + declareSkinlet< QskProgressRing, QskProgressRingSkinlet >(); declareSkinlet< QskRadioBox, QskRadioBoxSkinlet >(); const QFont font = QGuiApplication::font();