diff --git a/examples/gallery/progressbar/ProgressBarPage.cpp b/examples/gallery/progressbar/ProgressBarPage.cpp index fe0e7b07..3517eb91 100644 --- a/examples/gallery/progressbar/ProgressBarPage.cpp +++ b/examples/gallery/progressbar/ProgressBarPage.cpp @@ -159,6 +159,7 @@ void ProgressBarPage::populate() { auto* ring = new QskProgressRing( determinateRingsHBox ); ring->setSize( size ); + ring->setLayoutAlignmentHint( Qt::AlignCenter ); QQuickItem* parentItem; diff --git a/examples/iotdashboard/CMakeLists.txt b/examples/iotdashboard/CMakeLists.txt index 183c0112..afeffdb9 100644 --- a/examples/iotdashboard/CMakeLists.txt +++ b/examples/iotdashboard/CMakeLists.txt @@ -6,11 +6,8 @@ set(SOURCES Box.h Box.cpp BoxWithButtons.h BoxWithButtons.cpp - CircularProgressBar.h CircularProgressBar.cpp - CircularProgressBarSkinlet.h CircularProgressBarSkinlet.cpp Diagram.h Diagram.cpp DiagramSkinlet.h DiagramSkinlet.cpp - EnergyMeter.h EnergyMeter.cpp GraphicProvider.h GraphicProvider.cpp GridBox.h GridBox.cpp LightDisplaySkinlet.h LightDisplaySkinlet.cpp @@ -31,7 +28,7 @@ set(SOURCES UsageBox.h UsageBox.cpp UsageDiagram.h UsageDiagram.cpp StoragePage.h StoragePage.cpp - StorageMeter.h StorageMeter.cpp + ValueMeter.h ValueMeter.cpp StorageBar.h StorageBar.cpp StorageBarSkinlet.h StorageBarSkinlet.cpp nodes/DiagramDataNode.h nodes/DiagramDataNode.cpp diff --git a/examples/iotdashboard/CircularProgressBar.cpp b/examples/iotdashboard/CircularProgressBar.cpp deleted file mode 100644 index 479c5668..00000000 --- a/examples/iotdashboard/CircularProgressBar.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2021 Edelhirsch Software GmbH - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "CircularProgressBar.h" - -#include -#include - -QSK_SUBCONTROL( CircularProgressBar, Groove ) -QSK_SUBCONTROL( CircularProgressBar, Bar ) - -namespace -{ - class PositionAnimator : public QskAnimator - { - public: - PositionAnimator( CircularProgressBar* progressBar ) - : m_progressBar( progressBar ) - { - setAutoRepeat( true ); - setDuration( 1300 ); - - setWindow( progressBar->window() ); - } - - void advance( qreal value ) override - { - const auto aspect = CircularProgressBar::Bar | QskAspect::Position; - - m_progressBar->setMetric( aspect, value ); - m_progressBar->update(); - } - - private: - CircularProgressBar* m_progressBar; - }; -} - -class CircularProgressBar::PrivateData -{ - public: - void updateIndeterminateAnimator( CircularProgressBar* 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; -}; - -CircularProgressBar::~CircularProgressBar() = default; - -CircularProgressBar::CircularProgressBar( qreal min, qreal max, QQuickItem* parent ) - : QskBoundedControl( min, max, parent ) - , m_data( new PrivateData ) -{ - m_data->value = minimum(); - - initSizePolicy( QskSizePolicy::MinimumExpanding, QskSizePolicy::MinimumExpanding ); - - connect( this, &QskBoundedControl::boundariesChanged, - this, &CircularProgressBar::adjustValue ); -} - -CircularProgressBar::CircularProgressBar( QQuickItem* parent ) - : CircularProgressBar( 0.0, 100.0, parent ) -{ -} - -bool CircularProgressBar::isIndeterminate() const -{ - return m_data->isIndeterminate; -} - -void CircularProgressBar::setIndeterminate( bool on ) -{ - if ( on == m_data->isIndeterminate ) - return; - - m_data->isIndeterminate = on; - m_data->updateIndeterminateAnimator( this ); - - update(); - Q_EMIT indeterminateChanged( on ); -} - -void CircularProgressBar::resetOrigin() -{ - if ( m_data->hasOrigin ) - { - m_data->hasOrigin = false; - - update(); - Q_EMIT originChanged( origin() ); - } -} - -qreal CircularProgressBar::origin() const -{ - if ( m_data->hasOrigin ) - { - return boundedValue( m_data->origin ); - } - - return minimum(); -} - -qreal CircularProgressBar::value() const -{ - return m_data->value; -} - -qreal CircularProgressBar::valueAsRatio() const -{ - return QskBoundedControl::valueAsRatio( m_data->value ); -} - -void CircularProgressBar::setValue( qreal value ) -{ - if ( isComponentComplete() ) - value = boundedValue( value ); - - setValueInternal( value ); -} - -void CircularProgressBar::setValueAsRatio( qreal ratio ) -{ - ratio = qBound( 0.0, ratio, 1.0 ); - setValue( minimum() + ratio * boundaryLength() ); -} - -void CircularProgressBar::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 CircularProgressBar::componentComplete() -{ - Inherited::componentComplete(); - adjustValue(); -} - -void CircularProgressBar::setValueInternal( qreal value ) -{ - if ( !qskFuzzyCompare( value, m_data->value ) ) - { - m_data->value = value; - - Q_EMIT valueChanged( value ); - - update(); - } -} - -void CircularProgressBar::adjustValue() -{ - if ( isComponentComplete() ) - setValueInternal( boundedValue( m_data->value ) ); -} - -#include "moc_CircularProgressBar.cpp" diff --git a/examples/iotdashboard/CircularProgressBar.h b/examples/iotdashboard/CircularProgressBar.h deleted file mode 100644 index 9cd84898..00000000 --- a/examples/iotdashboard/CircularProgressBar.h +++ /dev/null @@ -1,61 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2021 Edelhirsch Software GmbH - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#pragma once - -#include - -class CircularProgressBar : public QskBoundedControl -{ - Q_OBJECT - - 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, Bar ) - - CircularProgressBar( qreal min, qreal max, QQuickItem* parent = nullptr ); - CircularProgressBar( QQuickItem* parent = nullptr ); - ~CircularProgressBar() override; - - bool isIndeterminate() const; - void setIndeterminate( bool on = true ); - - void resetOrigin(); - qreal origin() const; - - qreal value() const; - qreal valueAsRatio() const; // [0.0, 1.0] - - public Q_SLOTS: - void setValue( qreal ); - void setValueAsRatio( qreal ); - void setOrigin( qreal ); - - Q_SIGNALS: - void indeterminateChanged( bool ); - void valueChanged( qreal ); - void originChanged( qreal ); - - protected: - void componentComplete() override; - - private: - void setValueInternal( qreal value ); - void adjustValue(); - - class PrivateData; - std::unique_ptr< PrivateData > m_data; -}; diff --git a/examples/iotdashboard/CircularProgressBarSkinlet.cpp b/examples/iotdashboard/CircularProgressBarSkinlet.cpp deleted file mode 100644 index 0bd5f418..00000000 --- a/examples/iotdashboard/CircularProgressBarSkinlet.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2021 Edelhirsch Software GmbH - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "CircularProgressBarSkinlet.h" -#include "CircularProgressBar.h" - -CircularProgressBarSkinlet::CircularProgressBarSkinlet( QskSkin* skin ) - : QskSkinlet( skin ) -{ - setNodeRoles( { GrooveRole, BarRole } ); -} - -CircularProgressBarSkinlet::~CircularProgressBarSkinlet() -{ -} - -QRectF CircularProgressBarSkinlet::subControlRect( - const QskSkinnable*, const QRectF& contentsRect, QskAspect::Subcontrol ) const -{ - return contentsRect; -} - -QSGNode* CircularProgressBarSkinlet::updateSubNode( - const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const -{ - switch( nodeRole ) - { - case GrooveRole: - { - return updateArcNode( skinnable, node, CircularProgressBar::Groove ); - } - case BarRole: - { - const auto bar = static_cast< const CircularProgressBar* >( skinnable ); - - const qreal startAngle = 90.0; - const qreal spanAngle = 360.0 * bar->valueAsRatio(); - - return updateArcNode( skinnable, node, startAngle, -spanAngle, - CircularProgressBar::Bar ); - } - } - - return Inherited::updateSubNode( skinnable, nodeRole, node ); -} - -#include "moc_CircularProgressBarSkinlet.cpp" diff --git a/examples/iotdashboard/CircularProgressBarSkinlet.h b/examples/iotdashboard/CircularProgressBarSkinlet.h deleted file mode 100644 index 9075fbb3..00000000 --- a/examples/iotdashboard/CircularProgressBarSkinlet.h +++ /dev/null @@ -1,36 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2021 Edelhirsch Software GmbH - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#pragma once - -#include - -class CircularProgressBar; - -class CircularProgressBarSkinlet : public QskSkinlet -{ - Q_GADGET - - using Inherited = QskSkinlet; - - public: - enum NodeRole - { - GrooveRole, - BarRole, - - RoleCount, - }; - - Q_INVOKABLE CircularProgressBarSkinlet( QskSkin* = nullptr ); - ~CircularProgressBarSkinlet() override; - - QRectF subControlRect( const QskSkinnable*, - const QRectF&, QskAspect::Subcontrol ) const override; - - protected: - QSGNode* updateSubNode( const QskSkinnable*, - quint8 nodeRole, QSGNode* ) const override; -}; diff --git a/examples/iotdashboard/EnergyMeter.cpp b/examples/iotdashboard/EnergyMeter.cpp deleted file mode 100644 index f9d20537..00000000 --- a/examples/iotdashboard/EnergyMeter.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2021 Edelhirsch Software GmbH - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "EnergyMeter.h" -#include "CircularProgressBar.h" - -#include -#include - -namespace -{ - class ValueLabel : public QskTextLabel - { - public: - ValueLabel( QQuickItem* parent ) - : QskTextLabel( parent ) - { - initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); - setLayoutAlignmentHint( Qt::AlignCenter ); - setFontRole( QskFontRole::Caption ); - } - - void setValue( int value ) - { - setText( locale().toString( value ) + " " + locale().percent() ); - } - }; -} - -EnergyMeter::EnergyMeter( const QColor& textColor, - const QskGradient& gradient, int value, QQuickItem* parent ) - : QskControl( parent ) -{ - setAutoLayoutChildren( true ); - - auto valueBar = new CircularProgressBar( this ); - valueBar->setGradientHint( CircularProgressBar::Bar, gradient ); - valueBar->setValue( value ); - - auto valueLabel = new ValueLabel( this ); - valueLabel->setTextColor( textColor ); - valueLabel->setValue( value ); -} - -QSizeF EnergyMeter::contentsSizeHint( - Qt::SizeHint which, const QSizeF& constraint ) const -{ - if ( which != Qt::PreferredSize ) - return QSizeF(); - - qreal size; - - if ( constraint.width() > 0 ) - { - size = constraint.width(); - } - else if ( constraint.height() > 0 ) - { - size = constraint.height(); - } - else - { - size = 57; - } - - return QSizeF( size, size ); -} diff --git a/examples/iotdashboard/EnergyMeter.h b/examples/iotdashboard/EnergyMeter.h deleted file mode 100644 index 7780dd9e..00000000 --- a/examples/iotdashboard/EnergyMeter.h +++ /dev/null @@ -1,18 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2021 Edelhirsch Software GmbH - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#pragma once - -#include - -class EnergyMeter : public QskControl -{ - public: - EnergyMeter( const QColor&, const QskGradient&, - int progress, QQuickItem* parent = nullptr ); - - protected: - QSizeF contentsSizeHint( Qt::SizeHint, const QSizeF& ) const override; -}; diff --git a/examples/iotdashboard/Skin.cpp b/examples/iotdashboard/Skin.cpp index d3e9dbaf..09b7b3c3 100644 --- a/examples/iotdashboard/Skin.cpp +++ b/examples/iotdashboard/Skin.cpp @@ -7,8 +7,6 @@ #include "Box.h" #include "BoxWithButtons.h" -#include "CircularProgressBar.h" -#include "CircularProgressBarSkinlet.h" #include "DashboardPage.h" #include "Diagram.h" #include "DiagramSkinlet.h" @@ -21,7 +19,6 @@ #include "RoundedIcon.h" #include "StorageBar.h" #include "StorageBarSkinlet.h" -#include "StorageMeter.h" #include "StoragePage.h" #include "TopBar.h" #include "UsageBox.h" @@ -37,6 +34,7 @@ #include #include #include +#include #include #include @@ -56,7 +54,6 @@ Skin::Skin( QObject* parent ) { setObjectName( "iot" ); - declareSkinlet< CircularProgressBar, CircularProgressBarSkinlet >(); declareSkinlet< Diagram, DiagramSkinlet >(); declareSkinlet< LightDisplay, LightDisplaySkinlet >(); declareSkinlet< StorageBar, StorageBarSkinlet >(); @@ -87,9 +84,9 @@ void Skin::initHints() ed.setPadding( MainContentGridBox::Panel, { 19, 0, 27, 24 } ); - // menu bar: - { + // menu bar: + using Q = QskPushButton; using A = QskAspect; @@ -113,25 +110,27 @@ void Skin::initHints() ed.setAlignment( Q::Icon | A::Header, Qt::AlignCenter ); } - // top bar: - ed.setPadding( TopBar::Panel, { 25, 35, 25, 0 } ); + { + // top bar: - ed.setColor( TopBarItem::Item1 | QskAspect::TextColor, 0xffff3122 ); - ed.setColor( TopBarItem::Item2 | QskAspect::TextColor, 0xff6776ff ); - ed.setColor( TopBarItem::Item3 | QskAspect::TextColor, 0xfff99055 ); - ed.setColor( TopBarItem::Item4 | QskAspect::TextColor, 0xff6776ff ); + ed.setPadding( TopBar::Panel, { 25, 35, 25, 0 } ); - // arcs are counterclockwise, so specify the 2nd color first: - ed.setGradient( TopBarItem::Item1, 0xffff3122, 0xffff5c00 ); - ed.setGradient( TopBarItem::Item2, 0xff6100ff, 0xff6776ff ); - ed.setGradient( TopBarItem::Item3, 0xffff3122, 0xffffce50 ); - ed.setGradient( TopBarItem::Item4, 0xff6100ff, 0xff6776ff ); + ed.setColor( TopBarItem::Item1 | QskAspect::TextColor, 0xffff3122 ); + ed.setColor( TopBarItem::Item2 | QskAspect::TextColor, 0xff6776ff ); + ed.setColor( TopBarItem::Item3 | QskAspect::TextColor, 0xfff99055 ); + ed.setColor( TopBarItem::Item4 | QskAspect::TextColor, 0xff6776ff ); + + ed.setGradient( TopBarItem::Item1, 0xffff5c00, 0xffff3122 ); + ed.setGradient( TopBarItem::Item2, 0xff6776ff, 0xff6100ff ); + ed.setGradient( TopBarItem::Item3, 0xffffce50, 0xffff3122 ); + ed.setGradient( TopBarItem::Item4, 0xff6776ff, 0xff6100ff ); + } // the bar gradient is defined through the top bar items above - ed.setArcMetrics( CircularProgressBar::Groove, 90, -360, 8.53 ); + ed.setArcMetrics( QskProgressRing::Groove, 90, -360, 8.53 ); // the span angle will be set in the progress bar, we just give a dummy // value here: - ed.setArcMetrics( CircularProgressBar::Bar, 90, -180, 8.53 ); + ed.setArcMetrics( QskProgressRing::Fill, ed.arcMetrics( QskProgressRing::Groove ) ); ed.setFontRole( TimeTitleLabel::Text, { QskFontRole::Caption, QskFontRole::High } ); @@ -283,8 +282,8 @@ void Skin::initHints() ed.setColor( QskTextLabel::Text, palette.text ); ed.setColor( UsageDiagramBox::DayText, palette.text ); - ed.setMetric( CircularProgressBar::Groove | QskAspect::Border, 2 ); - ed.setColor( CircularProgressBar::Groove | QskAspect::Border, + ed.setMetric( QskProgressRing::Groove | QskAspect::Border, 2 ); + ed.setColor( QskProgressRing::Groove | QskAspect::Border, palette.circularProgressBarGroove ); // storage bar @@ -302,9 +301,13 @@ void Skin::initHints() // storage meter { - ed.setGradient( StorageMeter::Status, - { { { 0.00, "#00ff00" }, { 0.33, "#00ff00" }, { 0.33, "#ffaf00" }, { 0.66, "#ffaf00" }, - { 0.66, "#ff0000" }, { 1.00, "#ff0000" } } } ); + ed.setGradient( StoragePage::Status, + { { + { 0.00, "#00ff00" }, { 0.33, "#00ff00" }, + { 0.33, "#ffaf00" }, { 0.66, "#ffaf00" }, + { 0.66, "#ff0000" }, { 1.00, "#ff0000" } + } } + ); } } diff --git a/examples/iotdashboard/StorageMeter.cpp b/examples/iotdashboard/StorageMeter.cpp deleted file mode 100644 index af9d528b..00000000 --- a/examples/iotdashboard/StorageMeter.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2022 Edelhirsch Software GmbH - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "StorageMeter.h" -#include "CircularProgressBar.h" -#include -#include - -QSK_SUBCONTROL( StorageMeter, Status ) - -namespace -{ - inline QString make_text( const QLocale& locale, const qreal value ) - { - return locale.toString( static_cast< int >( value ) ) + " " + locale.percent(); - } -} - -StorageMeter::StorageMeter( QQuickItem* parent ) noexcept - : CircularProgressBar( parent ) - , label( new QskTextLabel( this ) ) -{ - setAutoLayoutChildren( true ); - setSizePolicy( QskSizePolicy::Preferred, QskSizePolicy::Constrained ); - - label->setText( make_text( locale(), value() ) ); - label->setSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); - label->setLayoutAlignmentHint( Qt::AlignCenter ); - label->setFontRole( QskFontRole::Caption ); -} - -void StorageMeter::setValue( const qreal value ) -{ - const auto gradient = gradientHint( StorageMeter::Status ); - const auto color = gradient.extracted( value / 100.0, value / 100.0 ).startColor(); - setGradientHint( StorageMeter::Bar, { color, color.lighter() } ); - CircularProgressBar::setValue( value ); - label->setTextColor( color ); - label->setText( make_text( locale(), value ) ); -} - -QSizeF StorageMeter::contentsSizeHint( Qt::SizeHint which, const QSizeF& constraint ) const -{ - if ( which != Qt::PreferredSize ) - return QSizeF(); - - qreal size; - - if ( constraint.width() > 0 ) - { - size = constraint.width(); - } - else if ( constraint.height() > 0 ) - { - size = constraint.height(); - } - else - { - size = 57; - } - - return QSizeF( size, size ); -} diff --git a/examples/iotdashboard/StorageMeter.h b/examples/iotdashboard/StorageMeter.h deleted file mode 100644 index 16a8f454..00000000 --- a/examples/iotdashboard/StorageMeter.h +++ /dev/null @@ -1,22 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2022 Edelhirsch Software GmbH - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#pragma once - -#include "CircularProgressBar.h" -#include - -class StorageMeter final : public CircularProgressBar -{ - public: - QSK_SUBCONTROLS( Status ) - explicit StorageMeter( QQuickItem* parent = nullptr ) noexcept; - public Q_SLOTS: - void setValue( qreal value ); - - private: - QSizeF contentsSizeHint( Qt::SizeHint which, const QSizeF& constraint ) const override; - class QskTextLabel* label = nullptr; -}; diff --git a/examples/iotdashboard/StoragePage.cpp b/examples/iotdashboard/StoragePage.cpp index f1ff5b3c..038a6e39 100644 --- a/examples/iotdashboard/StoragePage.cpp +++ b/examples/iotdashboard/StoragePage.cpp @@ -6,7 +6,7 @@ #include "StoragePage.h" #include "Box.h" #include "StorageBar.h" -#include "StorageMeter.h" +#include "ValueMeter.h" #include #include #include @@ -20,6 +20,60 @@ #include QSK_SUBCONTROL( StoragePage, Panel ) +QSK_SUBCONTROL( StoragePage, Status ) + +namespace +{ + struct Storage + { + struct Media + { + qreal pictures = 0; + qreal music = 0; + qreal videos = 0; + qreal documents = 0; + qreal others = 0; + + inline constexpr bool operator==( const Media& rhs ) const noexcept + { + return pictures == rhs.pictures && music == rhs.music && videos == rhs.videos && + documents == rhs.documents && others == rhs.others; + } + + inline constexpr qreal free() const noexcept + { + return 1.0 - pictures - music - videos - documents - others; + } + }; + + QString title; + QString description; + Media distribution; + }; + + class StorageMeter final : public ValueMeter + { + public: + StorageMeter( QQuickItem* parent = nullptr ) + : ValueMeter( parent ) + { + setFixedSize( 64, 64 ); + + connect( this, &ValueMeter::valueChanged, + this, &StorageMeter::setStatusColor ); + } + + private: + void setStatusColor( qreal value ) + { + const auto color = qskInterpolatedColorAt( + gradientHint( StoragePage::Status ).stops(), value / 100.0 ); + + setFillGradient( { color, color.lighter() } ); + setTextColor( color ); + } + }; +} struct StorageRowAnimator final : public QObject, public QskAnimator { @@ -85,8 +139,6 @@ void StoragePage::addRow( const QString& title, const QString& description, const auto percent = 100.0 * ( 1.0 - storage.distribution.free() ); auto* const meter = new StorageMeter( left ); meter->setValue( percent ); - meter->setMinimumSize( 64, 64 ); - meter->setMaximumSize( 64, 64 ); auto* const maintitle = new QskTextLabel( storage.title, center ); maintitle->setFontRole( QskFontRole::Headline ); @@ -136,5 +188,6 @@ void StoragePage::addRow( const QString& title, const QString& description, bar->setDocuments( media.documents * v ); bar->setOthers( media.others * v ); }; - connect( sync, &QskPushButton::clicked, animator, [ animator ]() { animator->start(); } ); + connect( sync, &QskPushButton::clicked, + animator, [ animator ]() { animator->start(); } ); } diff --git a/examples/iotdashboard/StoragePage.h b/examples/iotdashboard/StoragePage.h index efb09d41..7f1d0146 100644 --- a/examples/iotdashboard/StoragePage.h +++ b/examples/iotdashboard/StoragePage.h @@ -7,44 +7,15 @@ #include #include -#include - -class QQuickItem; class StoragePage final : public QskLinearBox { public: - QSK_SUBCONTROLS( Panel ) - explicit StoragePage( QQuickItem* parent = nullptr ); + QSK_SUBCONTROLS( Panel, Status ) + + StoragePage( QQuickItem* parent = nullptr ); private: - struct Storage - { - struct Media - { - qreal pictures = 0; - qreal music = 0; - qreal videos = 0; - qreal documents = 0; - qreal others = 0; - - inline constexpr bool operator==( const Media& rhs ) const noexcept - { - return pictures == rhs.pictures && music == rhs.music && videos == rhs.videos && - documents == rhs.documents && others == rhs.others; - } - - inline constexpr qreal free() const noexcept - { - return 1.0 - pictures - music - videos - documents - others; - } - }; - - QString title; - QString description; - Media distribution; - }; - void addRow( const QString& title, const QString& description, qreal pictures, qreal music, qreal videos, qreal documents, qreal others ); }; diff --git a/examples/iotdashboard/TopBar.cpp b/examples/iotdashboard/TopBar.cpp index 9deb9089..0667f21e 100644 --- a/examples/iotdashboard/TopBar.cpp +++ b/examples/iotdashboard/TopBar.cpp @@ -4,7 +4,7 @@ *****************************************************************************/ #include "TopBar.h" -#include "EnergyMeter.h" +#include "ValueMeter.h" #include #include @@ -41,6 +41,22 @@ namespace return TopBarItem::Item4; } } + + class EnergyMeter : public ValueMeter + { + public: + EnergyMeter( const QColor& textColor, + const QskGradient& gradient, int value, QQuickItem* parent ) + : ValueMeter( parent ) + { + setFillGradient( gradient ); + setValue( value ); + setTextColor( textColor ); + + setFixedSize( 57, 57 ); + } + }; + } TopBarItem::TopBarItem( @@ -62,9 +78,7 @@ TopBarItem::TopBarItem( const auto subcontrol = subcontrolForIndex( index ); const auto textColor = color( subcontrol | QskAspect::TextColor ); - auto pieChart = new EnergyMeter( - textColor, gradient, progress, pieChartAndDisplay ); - pieChart->setSizePolicy( Qt::Horizontal, QskSizePolicy::Constrained ); + (void) new EnergyMeter( textColor, gradient, progress, pieChartAndDisplay ); auto display = new QskLinearBox( Qt::Vertical, pieChartAndDisplay ); display->setSpacing( 0 ); diff --git a/examples/iotdashboard/ValueMeter.cpp b/examples/iotdashboard/ValueMeter.cpp new file mode 100644 index 00000000..43f34dd2 --- /dev/null +++ b/examples/iotdashboard/ValueMeter.cpp @@ -0,0 +1,42 @@ +/****************************************************************************** + * Copyright (C) 2022 Edelhirsch Software GmbH + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "ValueMeter.h" + +#include +#include + +ValueMeter::ValueMeter( QQuickItem* parent ) + : QskProgressRing( parent ) +{ + setAutoLayoutChildren( true ); + initSizePolicy( QskSizePolicy::MinimumExpanding, QskSizePolicy::Constrained ); + + m_label = new QskTextLabel( this ); + m_label->setSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); + m_label->setLayoutAlignmentHint( Qt::AlignCenter ); + m_label->setFontRole( QskFontRole::Caption ); + + connect( this, &QskProgressRing::valueChanged, + this, &ValueMeter::updateMeter ); + + updateMeter( value() ); +} + +void ValueMeter::updateMeter( const qreal value ) +{ + m_label->setText( text( value ) ); +} + +QString ValueMeter::text( qreal value ) const +{ + value = static_cast< int >( value ); + return locale().toString( value ) + ' ' + locale().percent(); +} + +void ValueMeter::setTextColor( const QColor& color ) +{ + m_label->setTextColor( color ); +} diff --git a/examples/iotdashboard/ValueMeter.h b/examples/iotdashboard/ValueMeter.h new file mode 100644 index 00000000..52d31dd3 --- /dev/null +++ b/examples/iotdashboard/ValueMeter.h @@ -0,0 +1,25 @@ +/****************************************************************************** + * Copyright (C) 2022 Edelhirsch Software GmbH + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#pragma once + +#include + +class QskTextLabel; + +class ValueMeter : public QskProgressRing +{ + public: + ValueMeter( QQuickItem* parent = nullptr ); + + void setTextColor( const QColor& ); + + protected: + virtual QString text( qreal ) const; + + private: + void updateMeter( qreal value ); + QskTextLabel* m_label = nullptr; +}; diff --git a/src/controls/QskProgressBarSkinlet.cpp b/src/controls/QskProgressBarSkinlet.cpp index 74fe9e69..225fb44c 100644 --- a/src/controls/QskProgressBarSkinlet.cpp +++ b/src/controls/QskProgressBarSkinlet.cpp @@ -15,13 +15,13 @@ using Q = QskProgressBar; namespace { - QskIntervalF qskFillInterval( const Q* bar ) + QskIntervalF qskFillInterval( const QskProgressIndicator* indicator ) { qreal pos1, pos2; - if ( bar->isIndeterminate() ) + if ( indicator->isIndeterminate() ) { - const auto pos = bar->positionHint( QskProgressIndicator::Fill ); + const auto pos = indicator->positionHint( QskProgressIndicator::Fill ); static const QEasingCurve curve( QEasingCurve::InOutCubic ); @@ -32,10 +32,11 @@ namespace } else { - pos1 = bar->valueAsRatio( bar->origin() ); - pos2 = bar->valueAsRatio( bar->value() ); + 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() ) @@ -107,11 +108,11 @@ QSGNode* QskProgressBarSkinlet::updateFillNode( const auto subControl = Q::Fill; - const auto rect = bar->subControlRect( subControl ); + const auto rect = indicator->subControlRect( subControl ); if ( rect.isEmpty() ) return nullptr; - auto gradient = bar->gradientHint( subControl ); + auto gradient = indicator->gradientHint( subControl ); if ( !gradient.isVisible() ) return nullptr; diff --git a/src/controls/QskProgressRing.cpp b/src/controls/QskProgressRing.cpp index dcc1d8d6..af6c414b 100644 --- a/src/controls/QskProgressRing.cpp +++ b/src/controls/QskProgressRing.cpp @@ -4,7 +4,6 @@ *****************************************************************************/ #include "QskProgressRing.h" - #include "QskIntervalF.h" QSK_SUBCONTROL( QskProgressRing, Groove ) @@ -20,6 +19,8 @@ QskProgressRing::QskProgressRing( qreal min, qreal max, QQuickItem* parent ) : Inherited( min, max, parent ) , m_data( new PrivateData ) { + initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); + m_data->size = NormalSize; setSubcontrolProxy( Inherited::Groove, Groove ); diff --git a/src/controls/QskProgressRingSkinlet.cpp b/src/controls/QskProgressRingSkinlet.cpp index 86e90329..167cb861 100644 --- a/src/controls/QskProgressRingSkinlet.cpp +++ b/src/controls/QskProgressRingSkinlet.cpp @@ -10,31 +10,6 @@ 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 ) { @@ -48,27 +23,8 @@ 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 contentsRect; return Inherited::subControlRect( skinnable, contentsRect, subControl ); } @@ -90,36 +46,74 @@ QSGNode* QskProgressRingSkinlet::updateFillNode( if ( rect.isEmpty() ) return nullptr; + const auto metrics = ring->arcMetricsHint( subControl ); + if ( metrics.isNull() ) + return nullptr; + auto gradient = ring->gradientHint( subControl ); if ( !gradient.isVisible() ) return nullptr; + const auto intv = fillInterval( ring ); + 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 stops = qskExtractedGradientStops( gradient.stops(), + intv.lowerBound(), intv.upperBound() ); + + gradient.setStops( stops ); + + if ( metrics.spanAngle() < 0.0 ) + gradient.reverse(); } - 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(); + const auto startAngle = metrics.startAngle() + intv.lowerBound() * metrics.spanAngle(); + const auto spanAngle = intv.upperBound() * metrics.spanAngle(); return updateArcNode( ring, node, rect, gradient, startAngle, spanAngle, subControl ); } QSizeF QskProgressRingSkinlet::sizeHint( const QskSkinnable* skinnable, - Qt::SizeHint which, const QSizeF& ) const + Qt::SizeHint which, const QSizeF& constraint ) const { if ( which != Qt::PreferredSize ) return QSizeF(); - const auto ring = static_cast< const Q* >( skinnable ); + auto hint = skinnable->strutSizeHint( Q::Fill ); + hint = hint.expandedTo( skinnable->strutSizeHint( Q::Groove ) ); - const auto r = ring->strutSizeHint( Q::Fill ); - return r; + if ( !constraint.isEmpty() ) + { + const qreal aspectRatio = hint.isEmpty() ? 1.0 : hint.width() / hint.height(); + + if ( constraint.width() >= 0.0 ) + hint.setHeight( constraint.width() / aspectRatio ); + else + hint.setWidth( constraint.height() * aspectRatio ); + } + + 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 767930b1..c5dc815b 100644 --- a/src/controls/QskProgressRingSkinlet.h +++ b/src/controls/QskProgressRingSkinlet.h @@ -8,7 +8,7 @@ #include "QskProgressIndicatorSkinlet.h" -class QskProgressRing; +class QskIntervalF; class QSK_EXPORT QskProgressRingSkinlet : public QskProgressIndicatorSkinlet { @@ -29,6 +29,8 @@ 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