From 30363a5e41eae43c00f684c11558aff78302b447 Mon Sep 17 00:00:00 2001 From: Rick Vogel Date: Thu, 5 Jan 2023 17:08:50 +0100 Subject: [PATCH] refactoring --- examples/iotdashboard/CircularProgressBar.cpp | 2 + examples/iotdashboard/CircularProgressBar.h | 1 + examples/iotdashboard/Skin.cpp | 62 +++-- examples/iotdashboard/StorageBar.cpp | 221 +++++----------- examples/iotdashboard/StorageBar.h | 73 +++--- examples/iotdashboard/StorageBarSkinlet.cpp | 105 ++++++++ examples/iotdashboard/StorageBarSkinlet.h | 32 +++ examples/iotdashboard/StorageMeter.cpp | 83 +++--- examples/iotdashboard/StorageMeter.h | 22 +- examples/iotdashboard/StoragePage.cpp | 243 ++++++++---------- examples/iotdashboard/StoragePage.h | 42 ++- examples/iotdashboard/iotdashboard.pro | 2 + 12 files changed, 506 insertions(+), 382 deletions(-) create mode 100644 examples/iotdashboard/StorageBarSkinlet.cpp create mode 100644 examples/iotdashboard/StorageBarSkinlet.h diff --git a/examples/iotdashboard/CircularProgressBar.cpp b/examples/iotdashboard/CircularProgressBar.cpp index 9d23790b..14a6583c 100644 --- a/examples/iotdashboard/CircularProgressBar.cpp +++ b/examples/iotdashboard/CircularProgressBar.cpp @@ -74,6 +74,8 @@ class CircularProgressBar::PrivateData bool isIndeterminate = false; }; +CircularProgressBar::~CircularProgressBar() = default; + CircularProgressBar::CircularProgressBar( qreal min, qreal max, QQuickItem* parent ) : QskBoundedControl( min, max, parent ) , m_data( new PrivateData ) diff --git a/examples/iotdashboard/CircularProgressBar.h b/examples/iotdashboard/CircularProgressBar.h index cb873065..efb71b9d 100644 --- a/examples/iotdashboard/CircularProgressBar.h +++ b/examples/iotdashboard/CircularProgressBar.h @@ -28,6 +28,7 @@ class CircularProgressBar : public QskBoundedControl CircularProgressBar( qreal min, qreal max, QQuickItem* parent = nullptr ); CircularProgressBar( QQuickItem* parent = nullptr ); + ~CircularProgressBar(); bool isIndeterminate() const; void setIndeterminate( bool on = true ); diff --git a/examples/iotdashboard/Skin.cpp b/examples/iotdashboard/Skin.cpp index 3a04fcad..66efb80a 100644 --- a/examples/iotdashboard/Skin.cpp +++ b/examples/iotdashboard/Skin.cpp @@ -9,27 +9,31 @@ #include "BoxWithButtons.h" #include "CircularProgressBar.h" #include "CircularProgressBarSkinlet.h" +#include "DashboardPage.h" #include "Diagram.h" #include "DiagramSkinlet.h" #include "GridBox.h" #include "LightDisplay.h" #include "LightDisplaySkinlet.h" -#include "DashboardPage.h" #include "MenuBar.h" #include "RoomsPage.h" -#include "RoundedIcon.h" #include "RoundButton.h" +#include "RoundedIcon.h" +#include "StorageBar.h" +#include "StorageBarSkinlet.h" +#include "StorageMeter.h" #include "StoragePage.h" #include "TopBar.h" #include "UsageBox.h" #include "UsageDiagram.h" #include -#include -#include #include +#include +#include #include #include +#include #include #include #include @@ -43,7 +47,7 @@ namespace { QFont font( "Proxima Nova" ); - if( semiBold ) + if ( semiBold ) { font.setWeight( QFont::Bold ); } @@ -51,7 +55,6 @@ namespace font.setPointSizeF( pointSize /*/ qskDpiScaled( 1.0 )*/ ); return font; } - } Skin::Skin( const Palette& palette, QObject* parent ) @@ -60,6 +63,7 @@ Skin::Skin( const Palette& palette, QObject* parent ) declareSkinlet< CircularProgressBar, CircularProgressBarSkinlet >(); declareSkinlet< Diagram, DiagramSkinlet >(); declareSkinlet< LightDisplay, LightDisplaySkinlet >(); + declareSkinlet< StorageBar, StorageBarSkinlet >(); initHints( palette ); } @@ -84,8 +88,7 @@ void Skin::initHints( const Palette& palette ) QskSkinHintTableEditor ed( &hintTable() ); - ed.setPadding( MainContentGridBox::Panel, {19, 0, 27, 24} ); - + ed.setPadding( MainContentGridBox::Panel, { 19, 0, 27, 24 } ); // menu bar: ed.setMargin( MenuBarTopLabel::Graphic, { 50, 5, 50, 65 } ); @@ -110,7 +113,7 @@ void Skin::initHints( const Palette& palette ) // top bar: - ed.setPadding( TopBar::Panel, {25, 35, 25, 0} ); + ed.setPadding( TopBar::Panel, { 25, 35, 25, 0 } ); ed.setColor( TopBarItem::Item1 | QskAspect::TextColor, 0xffff3122 ); ed.setColor( TopBarItem::Item2 | QskAspect::TextColor, 0xff6776ff ); @@ -167,16 +170,20 @@ void Skin::initHints( const Palette& palette ) const int duration = 300; ed.setGradient( RoundedIcon::PalePanel | QskAbstractButton::Checked, pressedNormal ); - ed.setGradient( RoundedIcon::PalePanel | RoundedIcon::Bright | QskAbstractButton::Checked, pressedBright ); + ed.setGradient( + RoundedIcon::PalePanel | RoundedIcon::Bright | QskAbstractButton::Checked, + pressedBright ); ed.setAnimation( RoundedIcon::PalePanel | QskAspect::Color, duration ); ed.setGraphicRole( RoundedIcon::Graphic, RoundedIcon::NormalRole ); - ed.setGraphicRole( RoundedIcon::Graphic | QskAbstractButton::Checked, RoundedIcon::CheckedRole, - { QskStateCombination::CombinationNoState, RoundedIcon::Bright } ); + ed.setGraphicRole( RoundedIcon::Graphic | QskAbstractButton::Checked, + RoundedIcon::CheckedRole, + { QskStateCombination::CombinationNoState, RoundedIcon::Bright } ); ed.setAnimation( RoundedIcon::Graphic, duration ); QskColorFilter filter; - filter.addColorSubstitution( 0xff606675, palette.deviceGraphic ); // color comes from the SVG + filter.addColorSubstitution( + 0xff606675, palette.deviceGraphic ); // color comes from the SVG setGraphicFilter( RoundedIcon::CheckedRole, filter ); } @@ -268,7 +275,7 @@ void Skin::initHints( const Palette& palette ) ed.setGradient( RoundButton::Panel, palette.roundButton ); ed.setGradient( RoundButton::Panel | QskAbstractButton::Pressed, palette.roundButtonPressed, - { QskStateCombination::CombinationNoState, RoundButton::Top } ); + { QskStateCombination::CombinationNoState, RoundButton::Top } ); ed.setAnimation( RoundButton::Panel | QskAspect::Color, 100 ); ed.setBoxBorderColors( UsageDiagramBox::DaysBox, palette.weekdayBox ); @@ -278,6 +285,26 @@ void Skin::initHints( const Palette& palette ) auto grooveGradient = palette.circularProgressBarGroove; grooveGradient.setDirection( QskGradient::Linear ); ed.setGradient( CircularProgressBar::Groove, grooveGradient ); + + // storage bar + { + const auto make_gradient = []( const QColor color ) -> QskGradient { + return { color.lighter(), color }; + }; + ed.setGradient( StorageBar::Pictures, make_gradient( "#FFBE0B" ) ); + ed.setGradient( StorageBar::Music, make_gradient( "#FB5607" ) ); + ed.setGradient( StorageBar::Videos, make_gradient( "#FF006E" ) ); + ed.setGradient( StorageBar::Documents, make_gradient( "#8338EC" ) ); + ed.setGradient( StorageBar::Others, make_gradient( "#3A86FF" ) ); + ed.setGradient( StorageBar::Free, make_gradient( "lightgray" ) ); + } + + // storage meter + { + ed.setGradient( StorageMeter::Status, + { { { 0.00, "#00ff00" }, { 0.33, "#00ff00" }, { 0.33, "#ffaf00" }, { 0.66, "#ffaf00" }, + { 0.66, "#ff0000" }, { 1.00, "#ff0000" } } } ); + } } Skin::Palette DaytimeSkin::palette() const @@ -292,8 +319,8 @@ Skin::Palette DaytimeSkin::palette() const Qt::black, 0xffe5e5e5, 0xffc4c4c4, - { { { 0.0, 0xffff3122 }, { 0.2, 0xfffeeeb7 }, { 0.3, 0xffa7b0ff }, - { 0.5, 0xff6776ff }, { 1.0, Qt::black } } }, + { { { 0.0, 0xffff3122 }, { 0.2, 0xfffeeeb7 }, { 0.3, 0xffa7b0ff }, { 0.5, 0xff6776ff }, + { 1.0, Qt::black } } }, { { { 0.0, 0xffc4c4c4 }, { 0.5, 0xfff8f8f8 }, { 1.0, 0xffc4c4c4 } } }, 0xffdddddd, }; @@ -311,8 +338,7 @@ Skin::Palette NighttimeSkin::palette() const Qt::white, 0xff4a4a4a, 0xff555555, - { { { 0.0, 0xff991100 }, { 0.2, 0xff9a7a57 }, - { 0.5, 0xff3726af }, { 1.0, Qt::black } } }, + { { { 0.0, 0xff991100 }, { 0.2, 0xff9a7a57 }, { 0.5, 0xff3726af }, { 1.0, Qt::black } } }, { { { 0.0, 0xff666666 }, { 0.5, 0xff222222 }, { 1.0, 0xff333333 } } }, 0xff222222, }; diff --git a/examples/iotdashboard/StorageBar.cpp b/examples/iotdashboard/StorageBar.cpp index 1a9c261e..3a0a60e2 100644 --- a/examples/iotdashboard/StorageBar.cpp +++ b/examples/iotdashboard/StorageBar.cpp @@ -1,207 +1,118 @@ +/****************************************************************************** + * Copyright (C) 2022 Edelhirsch Software GmbH + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + #include "StorageBar.h" +#include #include #include #include -#include -QSK_SUBCONTROL( StorageBar, Pictures) -QSK_SUBCONTROL( StorageBar, Music) -QSK_SUBCONTROL( StorageBar, Videos) -QSK_SUBCONTROL( StorageBar, Documents) +QSK_SUBCONTROL( StorageBar, Pictures ) +QSK_SUBCONTROL( StorageBar, Music ) +QSK_SUBCONTROL( StorageBar, Videos ) +QSK_SUBCONTROL( StorageBar, Documents ) QSK_SUBCONTROL( StorageBar, Others ) QSK_SUBCONTROL( StorageBar, Free ) -class StorageBarSkinlet final : public QskSkinlet +using S = StorageBar; + +StorageBar::StorageBar( QskQuickItem* const parent ) + : Inherited( parent ) { - Q_GADGET -public: + static constexpr qreal size = 16.0; + static constexpr qreal radius = size / 2.0; - using Inherited = QskSkinlet; - enum NodeRole { Pictures, Music, Videos, Documents, Others, Free }; - Q_INVOKABLE StorageBarSkinlet( QskSkin* skin = nullptr ) : Inherited(skin) - { - setNodeRoles( { Pictures, Music, Videos, Documents, Others, Free } ); - setOwnedBySkinnable(true); - } + setMinimumSize( -1, size ); + setMaximumSize( -1, size ); -private: - - QRectF subControlRect( const QskSkinnable* skinnable, const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const override - { - using S = StorageBar; - const auto bar = static_cast< const S* >( skinnable ); - - auto x = contentsRect.x(); - const auto y = contentsRect.y(); - const auto h = contentsRect.height(); - - const QRectF p {x, y, contentsRect.width() * bar->pictures(), h}; - if(subControl == S::Pictures){ return p; } - x += p.width(); - - const QRectF m {x, y, contentsRect.width() * bar->music(), h}; - if(subControl == S::Music){ return m; } - x += m.width(); - - const QRectF v {x, y, contentsRect.width() * bar->videos(), h}; - if(subControl == S::Videos){ return v; } - x += v.width(); - - const QRectF d {x, y, contentsRect.width() * bar->documents(), h}; - if(subControl == S::Documents){ return d; } - x += d.width(); - - const QRectF o {x, y, contentsRect.width() * bar->others(), h}; - if(subControl == S::Others){ return o; } - x += o.width(); - - const QRectF f {x, y, contentsRect.width() * bar->free(), h}; - if(subControl == S::Free){ return f; } - - return Inherited::subControlRect( skinnable, contentsRect, subControl ); - } - - QSGNode* updateSubNode( const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node) const override - { - using S = StorageBar; - const auto bar = static_cast< const S* >( skinnable ); - - if ( nodeRole == Pictures) - { - const QskGradient g( { { 0.0, bar->color(S::Pictures).lighter() },{ 1.0, bar->color(S::Pictures) } } ); - return updateBoxNode( bar, node, bar->subControlRect( S::Pictures ), g, S::Pictures ); - } - - if ( nodeRole == Music) - { - const QskGradient g( { { 0.0, bar->color(S::Music).lighter() },{ 1.0, bar->color(S::Music) } } ); - return updateBoxNode( bar, node, bar->subControlRect( S::Music ), g, S::Music ); - } - - if ( nodeRole == Videos) - { - const QskGradient g( { { 0.0, bar->color(S::Videos).lighter() },{ 1.0, bar->color(S::Videos) } } ); - return updateBoxNode( bar, node, bar->subControlRect( S::Videos ), g, S::Videos ); - } - - if ( nodeRole == Documents) - { - const QskGradient g( { { 0.0, bar->color(S::Documents).lighter() },{ 1.0, bar->color(S::Documents) } } ); - return updateBoxNode( bar, node, bar->subControlRect( S::Documents ), g, S::Documents ); - } - - if ( nodeRole == Others) - { - const QskGradient g( { { 0.0, bar->color(S::Others).lighter() },{ 1.0, bar->color(S::Others) } } ); - return updateBoxNode( bar, node, bar->subControlRect( S::Others ), g, S::Others ); - } - - if ( nodeRole == Free) - { - const QskGradient g( { { 0.0, bar->color(S::Free).lighter() },{ 1.0, bar->color(S::Free)} } ); - return updateBoxNode( bar, node, bar->subControlRect( S::Free ), g, S::Free ); - } - - return Inherited::updateSubNode( skinnable, nodeRole, node ); - } -}; - -StorageBar::StorageBar(QskQuickItem * const parent) : Inherited(parent) -{ - setSkinlet(new StorageBarSkinlet()); - - using S = StorageBar; - - // TODO move into skin? - setColor(S::Pictures, "#FFBE0B"); - setColor(S::Music,"#FB5607"); - setColor(S::Videos,"#FF006E"); - setColor(S::Documents, "#8338EC"); - setColor(S::Others, "#3A86FF"); - setColor(S::Free, "lightgray"); - - static constexpr qreal size = 16.0; - static constexpr qreal radius = size / 2.0; - - setMinimumSize(-1, size); - setMaximumSize(-1, size); - - setBoxShapeHint(S::Pictures, {radius, 0.0, radius, 0.0}); - setBoxShapeHint(S::Free, { 0.0, radius, 0.0, radius}); + setBoxShapeHint( S::Pictures, { radius, 0.0, radius, 0.0 } ); + setBoxShapeHint( S::Free, { 0.0, radius, 0.0, radius } ); } qreal StorageBar::pictures() const { - return m_pictures; + return m_pictures; } -void StorageBar::setPictures(qreal newPictures) +void StorageBar::setPictures( qreal newPictures ) { - if (qFuzzyCompare(m_pictures, newPictures)) - {return;} + if ( qFuzzyCompare( m_pictures, newPictures ) ) + { + return; + } - m_pictures = newPictures; - Q_EMIT picturesChanged(m_pictures); - update(); + m_pictures = newPictures; + Q_EMIT picturesChanged( m_pictures ); + update(); } qreal StorageBar::music() const { - return m_music; + return m_music; } -void StorageBar::setMusic(qreal newMusic) +void StorageBar::setMusic( qreal newMusic ) { - if (qFuzzyCompare(m_music, newMusic)) - {return;} - m_music = newMusic; - Q_EMIT musicChanged(m_music); - update(); + if ( qFuzzyCompare( m_music, newMusic ) ) + { + return; + } + m_music = newMusic; + Q_EMIT musicChanged( m_music ); + update(); } qreal StorageBar::videos() const { - return m_videos; + return m_videos; } -void StorageBar::setVideos(qreal newVideos) +void StorageBar::setVideos( qreal newVideos ) { - if (qFuzzyCompare(m_videos, newVideos)) - {return;} - m_videos = newVideos; - Q_EMIT videosChanged(m_videos); - update(); + if ( qFuzzyCompare( m_videos, newVideos ) ) + { + return; + } + m_videos = newVideos; + Q_EMIT videosChanged( m_videos ); + update(); } qreal StorageBar::documents() const { - return m_documents; + return m_documents; } -void StorageBar::setDocuments(qreal newDocuments) +void StorageBar::setDocuments( qreal newDocuments ) { - if (qFuzzyCompare(m_documents, newDocuments)) - {return;} - m_documents = newDocuments; - Q_EMIT documentsChanged(m_documents); - update(); + if ( qFuzzyCompare( m_documents, newDocuments ) ) + { + return; + } + m_documents = newDocuments; + Q_EMIT documentsChanged( m_documents ); + update(); } qreal StorageBar::others() const { - return m_others; + return m_others; } -void StorageBar::setOthers(qreal newOthers) +void StorageBar::setOthers( qreal newOthers ) { - if (qFuzzyCompare(m_others, newOthers)) - {return;} - m_others = newOthers; - Q_EMIT othersChanged(m_others); - update(); + if ( qFuzzyCompare( m_others, newOthers ) ) + { + return; + } + m_others = newOthers; + Q_EMIT othersChanged( m_others ); + update(); } qreal StorageBar::free() const { - return 1.0 - m_pictures - m_music - m_videos - m_documents - m_others; + return 1.0 - m_pictures - m_music - m_videos - m_documents - m_others; } diff --git a/examples/iotdashboard/StorageBar.h b/examples/iotdashboard/StorageBar.h index f3466468..6d6dddf0 100644 --- a/examples/iotdashboard/StorageBar.h +++ b/examples/iotdashboard/StorageBar.h @@ -1,48 +1,51 @@ +/****************************************************************************** + * Copyright (C) 2022 Edelhirsch Software GmbH + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + #pragma once #include class StorageBar final : public QskControl { - using Inherited = QskControl; + using Inherited = QskControl; - Q_OBJECT - Q_PROPERTY(qreal pictures READ pictures WRITE setPictures NOTIFY picturesChanged) - Q_PROPERTY(qreal music READ music WRITE setMusic NOTIFY musicChanged) - Q_PROPERTY(qreal videos READ videos WRITE setVideos NOTIFY videosChanged) - Q_PROPERTY(qreal documents READ documents WRITE setDocuments NOTIFY documentsChanged) - Q_PROPERTY(qreal others READ others WRITE setOthers NOTIFY othersChanged) + Q_OBJECT + Q_PROPERTY( qreal pictures READ pictures WRITE setPictures NOTIFY picturesChanged ) + Q_PROPERTY( qreal music READ music WRITE setMusic NOTIFY musicChanged ) + Q_PROPERTY( qreal videos READ videos WRITE setVideos NOTIFY videosChanged ) + Q_PROPERTY( qreal documents READ documents WRITE setDocuments NOTIFY documentsChanged ) + Q_PROPERTY( qreal others READ others WRITE setOthers NOTIFY othersChanged ) -public: + public: + QSK_SUBCONTROLS( Pictures, Music, Videos, Documents, Others, Free ) + explicit StorageBar( QskQuickItem* parent = nullptr ); - QSK_SUBCONTROLS( Pictures, Music, Videos, Documents, Others, Free ) - explicit StorageBar(QskQuickItem* parent = nullptr); + qreal pictures() const; + void setPictures( qreal newPictures ); + qreal music() const; + void setMusic( qreal newMusic ); + qreal videos() const; + void setVideos( qreal newVideos ); + qreal documents() const; + void setDocuments( qreal newDocuments ); + qreal others() const; + void setOthers( qreal newOthers ); + qreal free() const; - qreal pictures() const; - void setPictures(qreal newPictures); - qreal music() const; - void setMusic(qreal newMusic); - qreal videos() const; - void setVideos(qreal newVideos); - qreal documents() const; - void setDocuments(qreal newDocuments); - qreal others() const; - void setOthers(qreal newOthers); - qreal free() const; + Q_SIGNALS: -Q_SIGNALS: + void picturesChanged( qreal value ); + void musicChanged( qreal value ); + void videosChanged( qreal value ); + void documentsChanged( qreal value ); + void othersChanged( qreal value ); - void picturesChanged(qreal value); - void musicChanged(qreal value); - void videosChanged(qreal value); - void documentsChanged(qreal value); - void othersChanged(qreal value); - -private: - - qreal m_pictures; - qreal m_music; - qreal m_videos; - qreal m_documents; - qreal m_others; + private: + qreal m_pictures{ 0.0 }; + qreal m_music{ 0.0 }; + qreal m_videos{ 0.0 }; + qreal m_documents{ 0.0 }; + qreal m_others{ 0.0 }; }; diff --git a/examples/iotdashboard/StorageBarSkinlet.cpp b/examples/iotdashboard/StorageBarSkinlet.cpp new file mode 100644 index 00000000..0bb200ad --- /dev/null +++ b/examples/iotdashboard/StorageBarSkinlet.cpp @@ -0,0 +1,105 @@ +/****************************************************************************** + * Copyright (C) 2022 Edelhirsch Software GmbH + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#include "StorageBarSkinlet.h" +#include "StorageBar.h" + +using S = StorageBar; + +StorageBarSkinlet::StorageBarSkinlet( QskSkin* skin ) + : Inherited( skin ) +{ + setNodeRoles( { Pictures, Music, Videos, Documents, Others, Free } ); +} + +QRectF StorageBarSkinlet::subControlRect( const QskSkinnable* skinnable, const QRectF& contentsRect, + QskAspect::Subcontrol subControl ) const +{ + const auto* const bar = static_cast< const S* >( skinnable ); + + auto x = contentsRect.x(); + const auto y = contentsRect.y(); + const auto w = contentsRect.width(); + const auto h = contentsRect.height(); + + // segement widths + const auto p = w * bar->pictures(); + const auto m = w * bar->music(); + const auto v = w * bar->videos(); + const auto d = w * bar->documents(); + const auto o = w * bar->others(); + const auto f = w * bar->free(); + + if ( subControl == S::Pictures ) + { + return { x, y, p, h }; + } + x += p; + + if ( subControl == S::Music ) + { + return { x, y, m, h }; + } + x += m; + + if ( subControl == S::Videos ) + { + return { x, y, v, h }; + } + x += v; + + if ( subControl == S::Documents ) + { + return { x, y, d, h }; + } + x += d; + + if ( subControl == S::Others ) + { + return { x, y, o, h }; + } + x += o; + + if ( subControl == S::Free ) + { + return { x, y, f, h }; + } + + return Inherited::subControlRect( skinnable, contentsRect, subControl ); +} + +namespace +{ + inline QSGNode* updateSegmentBoxNode( + const S* const skinnable, const QskAspect::Subcontrol& subcontrol, QSGNode* const node ) + { + return QskSkinlet::updateBoxNode( skinnable, node, skinnable->subControlRect( subcontrol ), + skinnable->gradientHint( subcontrol ), subcontrol ); + } +} + +QSGNode* StorageBarSkinlet::updateSubNode( + const QskSkinnable* const skinnable, const quint8 nodeRole, QSGNode* const node ) const +{ + const auto* const bar = static_cast< const S* >( skinnable ); + + switch ( nodeRole ) + { + case Pictures: + return updateSegmentBoxNode( bar, S::Pictures, node ); + case Music: + return updateSegmentBoxNode( bar, S::Music, node ); + case Videos: + return updateSegmentBoxNode( bar, S::Videos, node ); + case Documents: + return updateSegmentBoxNode( bar, S::Documents, node ); + case Others: + return updateSegmentBoxNode( bar, S::Others, node ); + case Free: + return updateSegmentBoxNode( bar, S::Free, node ); + default: + return Inherited::updateSubNode( skinnable, nodeRole, node ); + } +} diff --git a/examples/iotdashboard/StorageBarSkinlet.h b/examples/iotdashboard/StorageBarSkinlet.h new file mode 100644 index 00000000..1731d100 --- /dev/null +++ b/examples/iotdashboard/StorageBarSkinlet.h @@ -0,0 +1,32 @@ +/****************************************************************************** + * Copyright (C) 2022 Edelhirsch Software GmbH + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#pragma once + +#include + +class StorageBarSkinlet final : public QskSkinlet +{ + Q_GADGET + using Inherited = QskSkinlet; + + public: + enum NodeRole + { + Pictures, + Music, + Videos, + Documents, + Others, + Free + }; + Q_INVOKABLE StorageBarSkinlet( QskSkin* skin = nullptr ); + + private: + QRectF subControlRect( const QskSkinnable* skinnable, const QRectF& contentsRect, + QskAspect::Subcontrol subControl ) const override; + QSGNode* updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const override; +}; diff --git a/examples/iotdashboard/StorageMeter.cpp b/examples/iotdashboard/StorageMeter.cpp index fc6dd236..f1a59a30 100644 --- a/examples/iotdashboard/StorageMeter.cpp +++ b/examples/iotdashboard/StorageMeter.cpp @@ -1,46 +1,65 @@ +/****************************************************************************** + * Copyright (C) 2022 Edelhirsch Software GmbH + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + #include "StorageMeter.h" #include "CircularProgressBar.h" +#include #include -QSK_SUBCONTROL( StorageMeter, Fill ) +QSK_SUBCONTROL( StorageMeter, Status ) -StorageMeter::StorageMeter(QQuickItem *parent) noexcept - : EnergyMeter(QColor{}, QskGradient{}, 0, parent) +namespace { - const auto gradient = gradientHint(StorageMeter::Fill); - setGradientHint(StorageMeter::Fill, QskGradient(QskGradientStops{ - {0.0,Qt::green}, - {0.5,"darkorange"}, - {1.0,Qt::red} - })); + inline QString make_text( const QLocale& locale, const qreal value ) + { + return locale.toString( static_cast< int >( value ) ) + " " + locale.percent(); + } } -qreal StorageMeter::progress() const noexcept +StorageMeter::StorageMeter( QQuickItem* parent ) noexcept + : CircularProgressBar( parent ) + , label( new QskTextLabel( this ) ) { - if( auto* const bar = findChild() ) - { - return bar->value() / 100.0; - } - return 0.0; + 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( QskSkin::SmallFont ); } -void StorageMeter::setProgress(qreal progress) noexcept +void StorageMeter::setValue( const qreal value ) { - const auto value = qBound(0.0, progress, 1.0); - - const auto gradient = gradientHint(StorageMeter::Fill); - - const auto color = gradient.extracted( value, value ).startColor(); - - if( auto* const bar = findChild() ) - { - bar->setGradientHint( CircularProgressBar::Bar, {color, color.darker(100)}); - bar->setValue(value * 100.0); - } - - if ( auto* const label = findChild() ) - { + 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( locale().toString( static_cast(value * 100.0) ) + " " + locale().percent() ); - } + 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 index 3f4ed665..402f5c45 100644 --- a/examples/iotdashboard/StorageMeter.h +++ b/examples/iotdashboard/StorageMeter.h @@ -1,14 +1,22 @@ +/****************************************************************************** + * Copyright (C) 2022 Edelhirsch Software GmbH + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + #pragma once -#include "EnergyMeter.h" -#include +#include "CircularProgressBar.h" +#include -class StorageMeter final : public EnergyMeter +class StorageMeter final : public CircularProgressBar { public: - QSK_SUBCONTROLS( Fill ) - explicit StorageMeter(QQuickItem* parent = nullptr) noexcept; - qreal progress() const noexcept; + QSK_SUBCONTROLS( Status ) + explicit StorageMeter( QQuickItem* parent = nullptr ) noexcept; public Q_SLOTS: - void setProgress(qreal progress) noexcept; + 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 36c9ef7f..a3351b55 100644 --- a/examples/iotdashboard/StoragePage.cpp +++ b/examples/iotdashboard/StoragePage.cpp @@ -1,148 +1,133 @@ +/****************************************************************************** + * Copyright (C) 2022 Edelhirsch Software GmbH + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + #include "StoragePage.h" -#include "CircularProgressBar.h" #include "Box.h" -#include "EnergyMeter.h" +#include "CircularProgressBar.h" #include "Diagram.h" -#include "StorageMeter.h" +#include "EnergyMeter.h" #include "StorageBar.h" +#include "StorageMeter.h" +#include #include -#include -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include #include -#include +#include +#include +#include QSK_SUBCONTROL( StoragePage, Panel ) -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; - } -}; - -StoragePage::StoragePage(QQuickItem * const parent) : QskLinearBox(Qt::Vertical, parent) +struct StorageRowAnimator final : public QObject, public QskAnimator { - const auto createProgressBar = [](const qreal value, QQuickItem* const parent) - { - auto* const bar = new StorageMeter(parent); - bar->setProgress(value); - bar->setMinimumSize(16,16); - bar->setMaximumSize(64,64); - bar->setSizePolicy( QskSizePolicy::Preferred, QskSizePolicy::Constrained ); - return bar; - }; - - const auto createLinearBox = [](Qt::Orientation orientation = Qt::Vertical, QskSizePolicy::Policy hp = QskSizePolicy::Fixed , QskSizePolicy::Policy vp = QskSizePolicy::Fixed, QQuickItem* const parent = nullptr){ - auto* const layout = new QskLinearBox(orientation, parent); - layout->setSizePolicy( hp, vp ); - return layout; - }; - - const auto createRow = [&](const QString& titleText, const QString& subTitleText, const Media& media, QQuickItem* const parent){ - auto* const box = new Box("Network Storage", parent); - auto* const layout = new QskLinearBox(Qt::Horizontal, box); - auto* const left = new QskLinearBox(Qt::Vertical, layout); - auto* const center = new QskLinearBox(Qt::Vertical, layout); - auto* const right = new QskLinearBox(Qt::Vertical, layout); - auto* const stack = new QskStackBox(left); - stack->setAutoLayoutChildren(true); - const auto percent = 1.0 - media.free(); - auto* const bar = createProgressBar(percent, createLinearBox(Qt::Vertical, QskSizePolicy::Preferred, QskSizePolicy::Preferred, stack)); - - stack->addItem(bar); - stack->setCurrentItem(bar); - auto* title = new QskTextLabel(titleText, center); - title->setFontRole(QskSkin::LargeFont); - auto* subtitle = new QskTextLabel(subTitleText, center); - subtitle->setFontRole(QskSkin::MediumFont); - center->addSpacer(1,99); - - auto* const storageBar = new StorageBar(right); - storageBar->setPictures(media.pictures); - storageBar->setMusic(media.music); - storageBar->setVideos(media.videos); - storageBar->setDocuments(media.documents); - storageBar->setOthers(media.others); - - auto* const mediaLegend = new QskLinearBox(Qt::Horizontal, right); - mediaLegend->setSpacing(12); - mediaLegend->addSpacer(1,999); - auto* const sync = new QskPushButton("Update", mediaLegend); - sync->setFontRoleHint(QskPushButton::Text, QskSkin::SmallFont); - connect(sync, &QskPushButton::clicked, storageBar, [bar, storageBar, media](){ - struct PropertyAnimator final : public QskAnimator - { - void advance( qreal value ) override { callback(value); } - void done() override { delete this; } - std::function callback = [](qreal){}; - }; - - auto* const animator = new PropertyAnimator(); - animator->setEasingCurve(QEasingCurve::InQuad); - animator->setWindow(storageBar->window()); - animator->callback = [bar, storageBar, media](qreal v){ - bar->setProgress((1.0 - media.free()) * v); - storageBar->setPictures(media.pictures * v); - storageBar->setMusic(media.music * v); - storageBar->setVideos(media.videos * v); - storageBar->setDocuments(media.documents * v); - storageBar->setOthers(media.others * v); - }; - animator->setDuration(400); - animator->start(); - }); - - using S = StorageBar; - const auto subcontrols = QVector{S::Pictures, S::Music, S::Videos, S::Documents, S::Others, S::Free}; - const auto subcontrolNames = QStringList{"Picture", "Music", "Videos", "Documents", "Others", "Free"}; - Q_ASSERT(subcontrolNames.size() == subcontrols.size()); - - for(int i = 0; i < subcontrolNames.size(); ++i) + explicit StorageRowAnimator( QQuickWindow* const window, QObject* parent ) + : QObject( parent ) { - auto* const dot = new QskBox(mediaLegend); - dot->setBoxShapeHint(QskBox::Panel,{4}); - dot->setMinimumSize(8,8); - dot->setMaximumSize(8,8); - const auto color = storageBar->color(subcontrols[i]); - dot->setGradientHint(QskBox::Panel, {color.lighter(), color}); - auto* const label = new QskTextLabel(subcontrolNames[i], mediaLegend); - label->setFontRole(QskSkin::SmallFont); + setEasingCurve( QEasingCurve::InQuad ); + setWindow( window ); + setDuration( 400 ); } - layout->setStretchFactor(left,1); - layout->setStretchFactor(center,2); - layout->setStretchFactor(right,5); - return layout; - }; + void advance( qreal value ) override + { + callback( value ); + } - setPanel( true ); - setSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Expanding ); - setDefaultAlignment( Qt::AlignTop ); - setSpacing( 24 ); - setMargins(30); - setSizePolicy(Qt::Horizontal, QskSizePolicy::Minimum); + std::function< void( qreal ) > callback = []( qreal ) {}; +}; - setSubcontrolProxy( QskBox::Panel, StoragePage::Panel ); +StoragePage::StoragePage( QQuickItem* const parent ) + : QskLinearBox( Qt::Vertical, parent ) +{ + setPanel( true ); + setSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Expanding ); + setDefaultAlignment( Qt::AlignTop ); + setSpacing( 24 ); + setMargins( 30 ); + setSizePolicy( Qt::Horizontal, QskSizePolicy::Minimum ); - createRow("Backup (B:)","Used for daily backups", Media{0.1,0.1,0.1,0.02, 0.01}, this); - createRow("Share (S:)","Used for sharing files publicly",Media{0.05,0.05,0.2,0.2,0.01}, this); - createRow("Exchange (X:)","Used for exchanging large files", Media{0.1,0.1,0.1,0.1,0.5}, this); - addSpacer(1, 99); + setSubcontrolProxy( QskBox::Panel, StoragePage::Panel ); + + addRow( { "Backup (B:)", "Used for daily backups", { 0.1, 0.1, 0.1, 0.02, 0.01 } } ); + addRow( { "Share (S:)", "Used for sharing files publicly", { 0.05, 0.05, 0.2, 0.2, 0.01 } } ); + addRow( { "Exchange (X:)", "Used for exchanging large files", { 0.1, 0.1, 0.1, 0.1, 0.5 } } ); + + addSpacer( 1, 99 ); +} + +void StoragePage::addRow( Storage storage ) +{ + auto* const box = new Box( "Network Storage", this ); + auto* const layout = new QskLinearBox( Qt::Horizontal, box ); + auto* const left = new QskLinearBox( Qt::Vertical, layout ); + left->setDefaultAlignment( Qt::AlignCenter ); + auto* const center = new QskLinearBox( Qt::Vertical, layout ); + left->setDefaultAlignment( Qt::AlignLeft ); + auto* const right = new QskLinearBox( Qt::Vertical, layout ); + layout->setStretchFactor( left, 1 ); + layout->setStretchFactor( center, 2 ); + layout->setStretchFactor( right, 5 ); + + 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 title = new QskTextLabel( storage.title, center ); + title->setFontRole( QskSkin::LargeFont ); + auto* const subtitle = new QskTextLabel( storage.description, center ); + subtitle->setFontRole( QskSkin::MediumFont ); + + const auto media = storage.distribution; + + auto* const bar = new StorageBar( right ); + bar->setPictures( media.pictures ); + bar->setMusic( media.music ); + bar->setVideos( media.videos ); + bar->setDocuments( media.documents ); + bar->setOthers( media.others ); + + auto* const legend = new QskLinearBox( Qt::Horizontal, right ); + legend->setSpacing( 12 ); + legend->addSpacer( 1, 999 ); + auto* const sync = new QskPushButton( "Update", legend ); + sync->setFontRoleHint( QskPushButton::Text, QskSkin::SmallFont ); + + using S = StorageBar; + for ( const auto& pair : QVector< QPair< QString, QskGradient > >{ + { QStringLiteral( "Picture" ), bar->gradientHint( S::Pictures ) }, + { QStringLiteral( "Music" ), bar->gradientHint( S::Music ) }, + { QStringLiteral( "Videos" ), bar->gradientHint( S::Videos ) }, + { QStringLiteral( "Documents" ), bar->gradientHint( S::Documents ) }, + { QStringLiteral( "Others" ), bar->gradientHint( S::Others ) }, + { QStringLiteral( "Free" ), bar->gradientHint( S::Free ) } } ) + { + constexpr int size = 8; + auto* const dot = new QskBox( legend ); + dot->setBoxShapeHint( QskBox::Panel, { size / 2 } ); + dot->setMinimumSize( size, size ); + dot->setMaximumSize( size, size ); + dot->setGradientHint( QskBox::Panel, pair.second ); + auto* const label = new QskTextLabel( pair.first, legend ); + label->setFontRole( QskSkin::SmallFont ); + } + + auto* const animator = new StorageRowAnimator( window(), sync ); + animator->callback = [ meter, bar, media ]( qreal v ) { + meter->setValue( 100 * ( 1.0 - media.free() ) * v ); + bar->setPictures( media.pictures * v ); + bar->setMusic( media.music * v ); + bar->setVideos( media.videos * v ); + bar->setDocuments( media.documents * v ); + bar->setOthers( media.others * v ); + }; + connect( sync, &QskPushButton::clicked, animator, [ animator ]() { animator->start(); } ); } diff --git a/examples/iotdashboard/StoragePage.h b/examples/iotdashboard/StoragePage.h index b94d6c2a..20b58255 100644 --- a/examples/iotdashboard/StoragePage.h +++ b/examples/iotdashboard/StoragePage.h @@ -1,19 +1,49 @@ /****************************************************************************** - * Copyright (C) 2021 Edelhirsch Software GmbH + * Copyright (C) 2022 Edelhirsch Software GmbH * This file may be used under the terms of the 3-clause BSD License *****************************************************************************/ #pragma once -#include +#include #include +#include -class StoragePage : public QskLinearBox +class QQuickItem; + +class StoragePage final : public QskLinearBox { - Q_OBJECT - public: QSK_SUBCONTROLS( Panel ) + explicit StoragePage( QQuickItem* parent = nullptr ); - StoragePage( QQuickItem* parent ); + 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( Storage storage ); }; diff --git a/examples/iotdashboard/iotdashboard.pro b/examples/iotdashboard/iotdashboard.pro index 0e70ac80..43ac0f32 100644 --- a/examples/iotdashboard/iotdashboard.pro +++ b/examples/iotdashboard/iotdashboard.pro @@ -31,6 +31,7 @@ SOURCES += \ StoragePage.cpp \ StorageMeter.cpp \ StorageBar.cpp \ + StorageBarSkinlet.cpp \ main.cpp \ SOURCES += \ @@ -69,6 +70,7 @@ HEADERS += \ StoragePage.h \ StorageMeter.h \ StorageBar.h \ + StorageBarSkinlet.h \ HEADERS += \ nodes/DiagramDataNode.h \