diff --git a/examples/iotdashboard/LightDisplay.cpp b/examples/iotdashboard/LightDisplay.cpp new file mode 100644 index 00000000..b0d29b58 --- /dev/null +++ b/examples/iotdashboard/LightDisplay.cpp @@ -0,0 +1,34 @@ +/****************************************************************************** + * Copyright (C) 2021 Edelhirsch Software GmbH + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#include "LightDisplay.h" +#include "Skin.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +QSK_SUBCONTROL( LightDisplay, Groove ) +QSK_SUBCONTROL( LightDisplay, ColdPart ) +QSK_SUBCONTROL( LightDisplay, WarmPart ) +QSK_SUBCONTROL( LightDisplay, ValueText ) +QSK_SUBCONTROL( LightDisplay, LeftLabel ) +QSK_SUBCONTROL( LightDisplay, RightLabel ) + +LightDisplay::LightDisplay( QQuickItem* parent ) + : QskBoundedControl( parent ) +{ + setAlignmentHint( LeftLabel, Qt::AlignRight ); +} + +#include "moc_LightDisplay.cpp" diff --git a/examples/iotdashboard/LightIntensity.h b/examples/iotdashboard/LightDisplay.h similarity index 60% rename from examples/iotdashboard/LightIntensity.h rename to examples/iotdashboard/LightDisplay.h index ccdb4755..96710a52 100644 --- a/examples/iotdashboard/LightIntensity.h +++ b/examples/iotdashboard/LightDisplay.h @@ -5,22 +5,14 @@ #pragma once -#include +#include -class QskTextLabel; - -class LightDisplay : public QskLinearBox +class LightDisplay : public QskBoundedControl { Q_OBJECT public: - QSK_SUBCONTROLS( Panel, ColdPart, WarmPart, ValueText ) + QSK_SUBCONTROLS( Groove, ColdPart, WarmPart, ValueText, LeftLabel, RightLabel ) LightDisplay( QQuickItem* parent = nullptr ); - - protected: - void updateLayout() override; - - private: - QskTextLabel* m_valueLabel; }; diff --git a/examples/iotdashboard/LightDisplaySkinlet.cpp b/examples/iotdashboard/LightDisplaySkinlet.cpp new file mode 100644 index 00000000..c29d5f2b --- /dev/null +++ b/examples/iotdashboard/LightDisplaySkinlet.cpp @@ -0,0 +1,124 @@ +/****************************************************************************** + * Copyright (C) 2021 Edelhirsch Software GmbH + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#include "LightDisplaySkinlet.h" +#include "LightDisplay.h" + +#include + +#include + +LightDisplaySkinlet::LightDisplaySkinlet( QskSkin* skin ) + : QskSkinlet( skin ) +{ + setNodeRoles( { GrooveRole, WarmPartRole, ColdPartRole, ValueTextRole, + LeftLabelRole, RightLabelRole } ); +} + +LightDisplaySkinlet::~LightDisplaySkinlet() +{ +} + +QRectF LightDisplaySkinlet::subControlRect( const QskSkinnable* skinnable, + const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const +{ + auto* display = static_cast< const LightDisplay* >( skinnable ); + QRectF rect = contentsRect; + + if( subControl == LightDisplay::Groove ) + { + QSizeF size = textLabelsSize( display ); + + const qreal x = size.width(); + const qreal w = contentsRect.width() - 2 * size.width(); + const qreal y = 0; + const qreal h = contentsRect.height(); + + const qreal diameter = qMin( w, h ); + + rect = QRectF( x, y, diameter, diameter ); + return rect; + } + else if( subControl == LightDisplay::LeftLabel ) + { + QRectF grooveRect = subControlRect( skinnable, contentsRect, LightDisplay::Groove ); + QSizeF size = textLabelsSize( display ); + + rect.setWidth( size.width() ); + + rect.setY( grooveRect.y() + ( grooveRect.height() - size.height() ) / 2 ); + rect.setHeight( size.height() ); + + return rect; + } + else if( subControl == LightDisplay::RightLabel ) + { + QRectF grooveRect = subControlRect( skinnable, contentsRect, LightDisplay::Groove ); + QSizeF size = textLabelsSize( display ); + + rect.setX( rect.right() - size.width() ); + + rect.setY( grooveRect.y() + ( grooveRect.height() - size.height() ) / 2 ); + rect.setHeight( size.height() ); + + return rect; + } + + return contentsRect; +} + +QSGNode* LightDisplaySkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + switch( nodeRole ) + { + case GrooveRole: + { + return updateArcNode( skinnable, node, 0, 5760, + LightDisplay::Groove ); + } + case WarmPartRole: + { +// const qreal startAngle = 90 * 16; +// const auto bar = static_cast< const LightDisplay* >( skinnable ); +// const qreal spanAngle = bar->valueAsRatio() * -5760; +// return updateArcNode( skinnable, node, startAngle, spanAngle, +// LightDisplay::Bar ); + } + case ColdPartRole: + { + return nullptr; + } + case ValueTextRole: + { + + return nullptr; + } + case LeftLabelRole: + { + return updateTextNode( skinnable, node, QStringLiteral( "0" ), {}, + LightDisplay::LeftLabel ); + } + case RightLabelRole: + { + return updateTextNode( skinnable, node, QStringLiteral( "100" ), {}, + LightDisplay::RightLabel ); + } + } + + return Inherited::updateSubNode( skinnable, nodeRole, node ); +} + +QSizeF LightDisplaySkinlet::textLabelsSize( const LightDisplay* display ) const +{ + QFont font = display->effectiveFont( LightDisplay::LeftLabel ); + QFontMetricsF fm( font ); + + qreal w = fm.width( QStringLiteral( "100" ) ); + qreal h = fm.height(); + return { w, h }; +} + +#include "moc_LightDisplaySkinlet.cpp" diff --git a/examples/iotdashboard/LightDisplaySkinlet.h b/examples/iotdashboard/LightDisplaySkinlet.h new file mode 100644 index 00000000..8db42faa --- /dev/null +++ b/examples/iotdashboard/LightDisplaySkinlet.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * Copyright (C) 2021 Edelhirsch Software GmbH + * This file may be used under the terms of the 3-clause BSD License + *****************************************************************************/ + +#pragma once + +#include + +class LightDisplay; + +class LightDisplaySkinlet : public QskSkinlet +{ + Q_GADGET + + using Inherited = QskSkinlet; + + public: + enum NodeRole + { + GrooveRole, + ColdPartRole, + WarmPartRole, + ValueTextRole, + LeftLabelRole, + RightLabelRole, + + RoleCount, + }; + + Q_INVOKABLE LightDisplaySkinlet( QskSkin* = nullptr ); + ~LightDisplaySkinlet() override; + + QRectF subControlRect( const QskSkinnable*, + const QRectF&, QskAspect::Subcontrol ) const override; + + protected: + QSGNode* updateSubNode( const QskSkinnable*, + quint8 nodeRole, QSGNode* ) const override; + + private: + QSizeF textLabelsSize( const LightDisplay* display ) const; +}; diff --git a/examples/iotdashboard/LightIntensity.cpp b/examples/iotdashboard/LightIntensity.cpp deleted file mode 100644 index 4e957fb8..00000000 --- a/examples/iotdashboard/LightIntensity.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2021 Edelhirsch Software GmbH - * This file may be used under the terms of the 3-clause BSD License - *****************************************************************************/ - -#include "LightIntensity.h" -#include "Skin.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -QSK_SUBCONTROL( LightDisplay, Panel ) -QSK_SUBCONTROL( LightDisplay, ColdPart ) -QSK_SUBCONTROL( LightDisplay, WarmPart ) -QSK_SUBCONTROL( LightDisplay, ValueText ) - -namespace -{ - class LightDimmer; - - QColor invertedColor( const QColor& c ) - { - QColor ret = { 255 - c.red(), 255 - c.green(), 255 - c.blue()}; - return ret; - } - - class LightDimmer : public QQuickPaintedItem - { - public: - LightDimmer( const QskGradient& coldGradient, - const QskGradient& warmGradient, QQuickItem* parent ); - - double thickness() const - { - return m_thickness; - } - - void setThickness( double thickness ) - { - m_thickness = thickness; - } - - QColor backgroundColor() const - { - return m_backgroundColor; - } - - void setBackgroundColor( const QColor& color ) - { - m_backgroundColor = color; - } - - QRadialGradient ringGradient() const - { - return m_ringGradient; - } - - void setRingGradient( const QRadialGradient& gradient ) - { - m_ringGradient = gradient; - } - - QRectF ringRect() const - { - const qreal r = qMin( width(), height() ) - 4; - return QRectF( 0.0, 0.0, r, r ); - } - - private: - void paint( QPainter* ) override; - void updateGradient(); - - double m_thickness = 17.57; - QColor m_backgroundColor; - QRadialGradient m_ringGradient; - QskGradient m_coldGradient; - QskGradient m_warmGradient; - }; - - // ### There must be an easier way to do this - class DimmerAnimator : public QskAnimator - { - public: - DimmerAnimator( LightDisplay* display, LightDimmer* dimmer ) - : m_display( display ) - , m_dimmer( dimmer ) - { - QQuickWindow* w = static_cast< QQuickWindow* >( qGuiApp->allWindows().at( 0 ) ); - setWindow( w ); - setDuration( 500 ); - setEasingCurve( QEasingCurve::Linear ); - } - - void setup() override - { - m_backgroundColor = m_display->color( LightDisplay::Panel ); - m_ringGradient = m_dimmer->ringGradient(); - } - - void advance( qreal value ) override - { - const QColor c = m_backgroundColor; - const QColor c2 = invertedColor( c ); - const QColor newColor = QskRgb::interpolated( c2, c, value ); - m_dimmer->setBackgroundColor( newColor ); - - QRadialGradient gradient = m_ringGradient; - QRadialGradient newGradient = gradient; - - for( const QGradientStop& stop : gradient.stops() ) - { - QColor c = stop.second; - QColor c2 = invertedColor( c ); - const QColor newColor = QskRgb::interpolated( c, c2, value ); - newGradient.setColorAt( stop.first, newColor ); - } - - m_dimmer->setRingGradient( newGradient ); - m_dimmer->update(); - } - - private: - QColor m_backgroundColor; - QRadialGradient m_ringGradient; - LightDisplay* m_display; - LightDimmer* m_dimmer; - }; -} - -LightDimmer::LightDimmer( const QskGradient& coldGradient, - const QskGradient& warmGradient, QQuickItem* parent ) - : QQuickPaintedItem( parent ) - , m_coldGradient( coldGradient ) - , m_warmGradient( warmGradient ) -{ - connect( this, &QQuickPaintedItem::widthChanged, - this, &LightDimmer::updateGradient ); - - connect( this, &QQuickPaintedItem::heightChanged, - this, &LightDimmer::updateGradient ); -} - -void LightDimmer::updateGradient() -{ - const auto sz = ringRect().size(); - - QRadialGradient ringGradient( sz.width() / 2, sz.height() / 2, 110 ); - QGradientStop stop1( 0.0, "#c0c0c0" ); - QGradientStop stop2( 0.5, "#f0f0f0" ); - QGradientStop stop3( 1.0, "#c0c0c0" ); - ringGradient.setStops( {stop1, stop2, stop3} ); - - m_ringGradient = ringGradient; -} - -void LightDimmer::paint( QPainter* painter ) -{ - const auto sz = ringRect().size(); - - const qreal knobDiameter = 15.65; - const qreal offset = ( thickness() - knobDiameter ) + 2; - - painter->setRenderHint( QPainter::Antialiasing, true ); - - QRectF outerRect( 0, offset, sz.width(), sz.height() ); - - painter->setBrush( m_ringGradient ); - - painter->setPen( m_backgroundColor ); - painter->drawEllipse( outerRect ); - - int startAngle = 16 * 180; - int middleAngle = 16 * -90; - int endAngle = 16 * -90; - - QLinearGradient coldGradient( {thickness(), 0.0}, {thickness(), thickness()} ); - coldGradient.setColorAt( 0, m_coldGradient.colorAt( 0 ) ); - coldGradient.setColorAt( 1, m_coldGradient.colorAt( 1 ) ); - painter->setBrush( coldGradient ); - painter->setPen( Qt::transparent ); - painter->drawPie( outerRect, startAngle, middleAngle ); - - QLinearGradient warmGradient( {thickness(), 0.0}, {thickness(), thickness()} ); - warmGradient.setColorAt( 0, m_warmGradient.colorAt( 0 ) ); - warmGradient.setColorAt( 1, m_warmGradient.colorAt( 1 ) ); - painter->setBrush( warmGradient ); - painter->drawPie( outerRect, 16 * 90, endAngle ); - - painter->setBrush( m_backgroundColor ); - painter->setPen( m_backgroundColor ); - QRectF innerRect( thickness() / 2, thickness() / 2 + offset, sz.width() - thickness(), sz.height() - thickness() ); - painter->drawEllipse( innerRect ); - - painter->setBrush( m_backgroundColor ); - painter->setPen( "#c4c4c4" ); - QRectF knobRect( ( sz.width() - knobDiameter ) / 2, 1, knobDiameter, knobDiameter ); - painter->drawEllipse( knobRect ); -} - -LightDisplay::LightDisplay( QQuickItem* parent ) - : QskLinearBox( Qt::Horizontal, parent ) -{ - setSubcontrolProxy( QskBox::Panel, LightDisplay::Panel ); - - auto leftLabel = new QskTextLabel( QString::number( 0 ), this ); - leftLabel->setSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); - - auto rightLabel = new QskTextLabel( QString::number( 100 ), this ); - rightLabel->setSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); - - auto dimmer = new LightDimmer( gradientHint( ColdPart ), gradientHint( WarmPart ), this ); - dimmer->setBackgroundColor( color( Panel ) ); - - m_valueLabel = new QskTextLabel( QString::number( 50 ) + "%", dimmer ); - m_valueLabel->setSubcontrolProxy( QskTextLabel::Text, LightDisplay::ValueText ); - - addItem( leftLabel ); - addItem( dimmer ); - addItem( rightLabel ); - - auto animator = new DimmerAnimator( this, dimmer ); - connect( qskSetup, &QskSetup::skinChanged, - [animator]() { animator->start(); } ); -} - -void LightDisplay::updateLayout() -{ - QskLinearBox::updateLayout(); - - auto dimmer = static_cast< const LightDimmer* >( m_valueLabel->parentItem() ); - - QRectF r; - r.setSize( m_valueLabel->sizeConstraint() ); - r.moveCenter( dimmer->ringRect().center() + QPointF( 0, 4 ) ); - - m_valueLabel->setGeometry( r ); -} - -#include "moc_LightIntensity.cpp" diff --git a/examples/iotdashboard/MainContent.cpp b/examples/iotdashboard/MainContent.cpp index 60df8234..4f41e3c0 100644 --- a/examples/iotdashboard/MainContent.cpp +++ b/examples/iotdashboard/MainContent.cpp @@ -8,7 +8,7 @@ #include "Box.h" #include "BoxWithButtons.h" #include "UsageDiagram.h" -#include "LightIntensity.h" +#include "LightDisplay.h" #include "MyDevices.h" #include "PieChart.h" #include "TopBar.h" diff --git a/examples/iotdashboard/Skin.cpp b/examples/iotdashboard/Skin.cpp index 049a2732..311114a0 100644 --- a/examples/iotdashboard/Skin.cpp +++ b/examples/iotdashboard/Skin.cpp @@ -11,7 +11,8 @@ #include "CircularProgressBarSkinlet.h" #include "Diagram.h" #include "DiagramSkinlet.h" -#include "LightIntensity.h" +#include "LightDisplay.h" +#include "LightDisplaySkinlet.h" #include "MainContent.h" #include "MenuBar.h" #include "PieChartPainted.h" @@ -54,6 +55,7 @@ Skin::Skin( const Palette& palette, QObject* parent ) { declareSkinlet< CircularProgressBar, CircularProgressBarSkinlet >(); declareSkinlet< Diagram, DiagramSkinlet >(); + declareSkinlet< LightDisplay, LightDisplaySkinlet >(); initHints( palette ); } @@ -180,6 +182,8 @@ void Skin::initHints( const Palette& palette ) ed.setColor( Diagram::ChartArea3, "#66ff7d34" ); // light intensity: + ed.setGradient( LightDisplay::Groove, Qt::magenta ); + ed.setArcMetrics( LightDisplay::Groove, { 10, 0, 5760 } ); ed.setGradient( LightDisplay::ColdPart, { Qt::Horizontal, "#a7b0ff", "#6776ff" } ); ed.setGradient( LightDisplay::WarmPart, { Qt::Horizontal, "#feeeb7", "#ff3122" } ); ed.setFontRole( LightDisplay::ValueText, QskSkin::LargeFont ); @@ -191,7 +195,7 @@ void Skin::initHints( const Palette& palette ) ed.setGradient( Box::Panel, palette.box ); ed.setGradient( BoxWithButtons::Panel, palette.box ); ed.setGradient( UsageDiagramBox::Panel, palette.box ); - ed.setColor( LightDisplay::Panel, palette.lightDisplay ); +// ed.setColor( LightDisplay::Panel, palette.lightDisplay ); ### ed.setGradient( RoundButton::Panel, palette.roundButton ); ed.setBoxBorderColors( UsageDiagramBox::DaysBox, palette.weekdayBox ); ed.setColor( QskTextLabel::Text, palette.text ); diff --git a/examples/iotdashboard/iotdashboard.pro b/examples/iotdashboard/iotdashboard.pro index 2064a376..5f99e0e3 100644 --- a/examples/iotdashboard/iotdashboard.pro +++ b/examples/iotdashboard/iotdashboard.pro @@ -10,7 +10,8 @@ SOURCES += \ Diagram.cpp \ DiagramSkinlet.cpp \ GraphicProvider.cpp \ - LightIntensity.cpp \ + LightDisplaySkinlet.cpp \ + LightDisplay.cpp \ MainContent.cpp \ MenuBar.cpp \ MyDevices.cpp \ @@ -40,7 +41,8 @@ HEADERS += \ Diagram.h \ DiagramSkinlet.h \ GraphicProvider.h \ - LightIntensity.h \ + LightDisplaySkinlet.h \ + LightDisplay.h \ MainContent.h \ MainWindow.h \ MenuBar.h \