Iot dashboard light intensity (#142)
* IOT dashboard: Make light dimmer use arc renderer * use shadow * add value text * add warm and cold parts * add knob * handle input * only move knob when drawing along the arc * improve input handling * add tickmarks * add tickmarks node * clean up a bit * Update screenshot of IOT dashboard for github site
This commit is contained in:
parent
8413651bda
commit
927d2dd51c
Binary file not shown.
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 95 KiB |
157
examples/iotdashboard/LightDisplay.cpp
Normal file
157
examples/iotdashboard/LightDisplay.cpp
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* 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 <QskArcMetrics.h>
|
||||||
|
#include <QskEvent.h>
|
||||||
|
|
||||||
|
#include <QtMath>
|
||||||
|
|
||||||
|
QSK_SUBCONTROL( LightDisplay, Panel )
|
||||||
|
QSK_SUBCONTROL( LightDisplay, Groove )
|
||||||
|
QSK_SUBCONTROL( LightDisplay, ColdAndWarmArc )
|
||||||
|
QSK_SUBCONTROL( LightDisplay, Tickmarks )
|
||||||
|
QSK_SUBCONTROL( LightDisplay, ValueText )
|
||||||
|
QSK_SUBCONTROL( LightDisplay, LeftLabel )
|
||||||
|
QSK_SUBCONTROL( LightDisplay, RightLabel )
|
||||||
|
QSK_SUBCONTROL( LightDisplay, Knob )
|
||||||
|
|
||||||
|
QSK_STATE( LightDisplay, Pressed, ( QskAspect::FirstUserState << 1 ) )
|
||||||
|
|
||||||
|
LightDisplay::LightDisplay( QQuickItem* parent )
|
||||||
|
: QskBoundedValueInput( parent )
|
||||||
|
{
|
||||||
|
setAlignmentHint( LeftLabel, Qt::AlignRight );
|
||||||
|
setAlignmentHint( ValueText, Qt::AlignCenter | Qt::AlignHCenter );
|
||||||
|
|
||||||
|
setBoundaries( 0, 100 );
|
||||||
|
|
||||||
|
// ### move to Skin:
|
||||||
|
setShadow( { 0, 20 } );
|
||||||
|
setShadowColor( 0xe5e5e5 );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LightDisplay::isPressed() const
|
||||||
|
{
|
||||||
|
return hasSkinState( Pressed );
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightDisplay::setShadow( const QskShadowMetrics& shadow )
|
||||||
|
{
|
||||||
|
m_shadow = shadow;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QskShadowMetrics& LightDisplay::shadow() const
|
||||||
|
{
|
||||||
|
return m_shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightDisplay::setGradient( const QskGradient& gradient )
|
||||||
|
{
|
||||||
|
m_gradient = gradient;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QskGradient& LightDisplay::gradient() const
|
||||||
|
{
|
||||||
|
return m_gradient;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightDisplay::setShadowColor( const QColor& color )
|
||||||
|
{
|
||||||
|
m_shadowColor = color;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor LightDisplay::shadowColor() const
|
||||||
|
{
|
||||||
|
return m_shadowColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightDisplay::mousePressEvent( QMouseEvent* event )
|
||||||
|
{
|
||||||
|
QRectF handleRect = subControlRect( LightDisplay::Knob );
|
||||||
|
|
||||||
|
if ( handleRect.contains( event->pos() ) )
|
||||||
|
{
|
||||||
|
setSkinStateFlag( Pressed );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QskBoundedValueInput::mousePressEvent( event );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightDisplay::mouseMoveEvent( QMouseEvent* event )
|
||||||
|
{
|
||||||
|
if ( !isPressed() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto mousePos = qskMousePosition( event );
|
||||||
|
const auto rect = subControlRect( ColdAndWarmArc );
|
||||||
|
|
||||||
|
bool arcContainsMousePos = arcContainsPoint( rect, mousePos );
|
||||||
|
|
||||||
|
if( !arcContainsMousePos )
|
||||||
|
{
|
||||||
|
setSkinStateFlag( Pressed, false );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QskArcMetrics metrics = arcMetricsHint( ColdAndWarmArc );
|
||||||
|
const qreal angle = angleFromPoint( rect, mousePos );
|
||||||
|
const qreal ratio = ( metrics.spanAngle() - angle * 16 ) / metrics.spanAngle();
|
||||||
|
setValueAsRatio( ratio );
|
||||||
|
}
|
||||||
|
|
||||||
|
void LightDisplay::mouseReleaseEvent( QMouseEvent* /*event*/ )
|
||||||
|
{
|
||||||
|
setSkinStateFlag( Pressed, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal LightDisplay::angleFromPoint( const QRectF& rect, const QPointF& point ) const
|
||||||
|
{
|
||||||
|
QPointF circlePos( point.x() - rect.center().x(),
|
||||||
|
rect.center().y() - point.y() );
|
||||||
|
|
||||||
|
const qreal atan = qAtan2( circlePos.y(), circlePos.x() );
|
||||||
|
const qreal angle = qRadiansToDegrees( atan );
|
||||||
|
return angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LightDisplay::arcContainsPoint( const QRectF& rect, const QPointF& point ) const
|
||||||
|
{
|
||||||
|
// putting this in an own function just because it might be useful
|
||||||
|
// at other places in the future
|
||||||
|
|
||||||
|
const QskArcMetrics metrics = arcMetricsHint( ColdAndWarmArc );
|
||||||
|
const int tolerance = 20;
|
||||||
|
|
||||||
|
// 1. check angle
|
||||||
|
QPointF circlePos( point.x() - rect.center().x(),
|
||||||
|
rect.center().y() - point.y() );
|
||||||
|
|
||||||
|
const qreal angle = angleFromPoint( rect, point );
|
||||||
|
const bool angleWithinRange = ( angle + tolerance ) > metrics.startAngle()
|
||||||
|
&& angle < ( metrics.startAngle() + metrics.spanAngle() - tolerance );
|
||||||
|
|
||||||
|
// 2. check whether point is on arc
|
||||||
|
const qreal radiusMax = rect.width() / 2;
|
||||||
|
const qreal arcWidth = metrics.width();
|
||||||
|
const qreal radiusMin = radiusMax - arcWidth;
|
||||||
|
|
||||||
|
const qreal polarRadius = qSqrt( qPow( circlePos.x(), 2 ) + qPow( circlePos.y(), 2 ) );
|
||||||
|
const bool pointOnArc = ( polarRadius + tolerance ) > radiusMin
|
||||||
|
&& ( polarRadius - tolerance ) < radiusMax;
|
||||||
|
|
||||||
|
bool ret = angleWithinRange && pointOnArc;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "moc_LightDisplay.cpp"
|
47
examples/iotdashboard/LightDisplay.h
Normal file
47
examples/iotdashboard/LightDisplay.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Copyright (C) 2021 Edelhirsch Software GmbH
|
||||||
|
* This file may be used under the terms of the 3-clause BSD License
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QskBoundedValueInput.h>
|
||||||
|
#include <QskBoxShapeMetrics.h>
|
||||||
|
#include <QskShadowMetrics.h>
|
||||||
|
|
||||||
|
class LightDisplay : public QskBoundedValueInput
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
QSK_SUBCONTROLS( Panel, Groove, ColdAndWarmArc, Tickmarks, ValueText,
|
||||||
|
LeftLabel, RightLabel, Knob ) // ### rename knob to handle?
|
||||||
|
QSK_STATES( Pressed )
|
||||||
|
|
||||||
|
LightDisplay( QQuickItem* parent = nullptr );
|
||||||
|
|
||||||
|
bool isPressed() const;
|
||||||
|
|
||||||
|
void setShadow( const QskShadowMetrics& );
|
||||||
|
const QskShadowMetrics& shadow() const;
|
||||||
|
|
||||||
|
void setGradient( const QskGradient& );
|
||||||
|
const QskGradient& gradient() const;
|
||||||
|
|
||||||
|
void setShadowColor( const QColor& );
|
||||||
|
QColor shadowColor() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void mousePressEvent( QMouseEvent* e ) override;
|
||||||
|
void mouseMoveEvent( QMouseEvent* e ) override;
|
||||||
|
void mouseReleaseEvent( QMouseEvent* e ) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
qreal angleFromPoint( const QRectF&, const QPointF& ) const;
|
||||||
|
bool arcContainsPoint( const QRectF&, const QPointF& ) const;
|
||||||
|
|
||||||
|
QskShadowMetrics m_shadow;
|
||||||
|
QColor m_shadowColor = Qt::black;
|
||||||
|
|
||||||
|
QskGradient m_gradient;
|
||||||
|
};
|
209
examples/iotdashboard/LightDisplaySkinlet.cpp
Normal file
209
examples/iotdashboard/LightDisplaySkinlet.cpp
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* 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 "nodes/BoxShadowNode.h"
|
||||||
|
#include "nodes/RadialTickmarksNode.h"
|
||||||
|
|
||||||
|
#include <QskArcMetrics.h>
|
||||||
|
#include <QskTextOptions.h>
|
||||||
|
|
||||||
|
#include <QFontMetrics>
|
||||||
|
#include <QtMath>
|
||||||
|
|
||||||
|
LightDisplaySkinlet::LightDisplaySkinlet( QskSkin* skin )
|
||||||
|
: QskSkinlet( skin )
|
||||||
|
{
|
||||||
|
setNodeRoles( { GrooveRole, PanelRole, ColdAndWarmArcRole, TickmarksRole,
|
||||||
|
ValueTextRole, LeftLabelRole, RightLabelRole, KnobRole } );
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
const qreal ticksSpacing = 4; // space between the ticks and the arc
|
||||||
|
|
||||||
|
if( subControl == LightDisplay::Groove
|
||||||
|
|| subControl == LightDisplay::Panel
|
||||||
|
|| subControl == LightDisplay::ValueText )
|
||||||
|
{
|
||||||
|
QSizeF textSize = textLabelsSize( display );
|
||||||
|
QskArcMetrics arcMetrics = display->arcMetricsHint( LightDisplay::ColdAndWarmArc );
|
||||||
|
const qreal ticksWidth = display->arcMetricsHint( LightDisplay::Tickmarks ).width() + ticksSpacing;
|
||||||
|
|
||||||
|
const qreal x = textSize.width() + arcMetrics.width() + ticksWidth;
|
||||||
|
const qreal w = contentsRect.width() - ( 2 * ( textSize.width() + arcMetrics.width() + ticksWidth ) );
|
||||||
|
const qreal y = arcMetrics.width() + ticksWidth;
|
||||||
|
const qreal h = contentsRect.height() - 2 * ( arcMetrics.width() + ticksWidth );
|
||||||
|
|
||||||
|
const qreal diameter = qMin( w, h );
|
||||||
|
|
||||||
|
rect = QRectF( x, y, diameter, diameter );
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
else if( subControl == LightDisplay::ColdAndWarmArc )
|
||||||
|
{
|
||||||
|
const QRectF panelRect = subControlRect( skinnable, contentsRect,
|
||||||
|
LightDisplay::Panel );
|
||||||
|
auto barWidth = display->arcMetricsHint( LightDisplay::ColdAndWarmArc ).width();
|
||||||
|
auto rect = panelRect.marginsAdded( { barWidth, barWidth, barWidth, barWidth } );
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
else if( subControl == LightDisplay::Tickmarks )
|
||||||
|
{
|
||||||
|
const QRectF arcRect = subControlRect( skinnable, contentsRect,
|
||||||
|
LightDisplay::ColdAndWarmArc );
|
||||||
|
const qreal ticksWidth = display->arcMetricsHint( LightDisplay::Tickmarks ).width() + ticksSpacing;
|
||||||
|
const QRectF rect = arcRect.marginsAdded( { ticksWidth, ticksWidth, ticksWidth, ticksWidth } );
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
else if( subControl == LightDisplay::LeftLabel )
|
||||||
|
{
|
||||||
|
const QRectF ticksRect = subControlRect( skinnable, contentsRect, LightDisplay::Tickmarks );
|
||||||
|
QSizeF size = textLabelsSize( display );
|
||||||
|
|
||||||
|
rect.setWidth( size.width() );
|
||||||
|
|
||||||
|
rect.setY( ticksRect.y() + ( ticksRect.height() - size.height() ) / 2 );
|
||||||
|
rect.setHeight( size.height() );
|
||||||
|
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
else if( subControl == LightDisplay::RightLabel )
|
||||||
|
{
|
||||||
|
QRectF ticksRect = subControlRect( skinnable, contentsRect, LightDisplay::Tickmarks );
|
||||||
|
QSizeF size = textLabelsSize( display );
|
||||||
|
|
||||||
|
rect.setX( ticksRect.x() + ticksRect.width() );
|
||||||
|
|
||||||
|
rect.setY( ticksRect.y() + ( ticksRect.height() - size.height() ) / 2 );
|
||||||
|
rect.setHeight( size.height() );
|
||||||
|
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
else if( subControl == LightDisplay::Knob )
|
||||||
|
{
|
||||||
|
QRectF arcRect = subControlRect( skinnable, contentsRect, LightDisplay::ColdAndWarmArc );
|
||||||
|
QskArcMetrics arcMetrics = display->arcMetricsHint( LightDisplay::ColdAndWarmArc );
|
||||||
|
QSizeF knobSize = display->strutSizeHint( LightDisplay::Knob );
|
||||||
|
|
||||||
|
const qreal radius = ( arcRect.width() - arcMetrics.width() ) / 2;
|
||||||
|
const qreal angle = display->valueAsRatio() * 180;
|
||||||
|
|
||||||
|
const qreal cos = qFastCos( qDegreesToRadians( angle ) );
|
||||||
|
const qreal sin = qFastSin( qDegreesToRadians( angle ) );
|
||||||
|
|
||||||
|
const auto x = arcRect.center().x() - knobSize.width() / 2 - radius * cos;
|
||||||
|
const auto y = arcRect.center().y() - knobSize.height() / 2 - radius * sin;
|
||||||
|
|
||||||
|
rect = QRectF( x, y, knobSize.width(), knobSize.height() );
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
return contentsRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSGNode* LightDisplaySkinlet::updateSubNode(
|
||||||
|
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
|
||||||
|
{
|
||||||
|
auto* display = static_cast< const LightDisplay* >( skinnable );
|
||||||
|
|
||||||
|
|
||||||
|
switch( nodeRole )
|
||||||
|
{
|
||||||
|
case PanelRole:
|
||||||
|
{
|
||||||
|
return updateBoxNode( skinnable, node, LightDisplay::Panel );
|
||||||
|
}
|
||||||
|
case GrooveRole:
|
||||||
|
{
|
||||||
|
const QRectF grooveRect = display->subControlRect( LightDisplay::Groove );
|
||||||
|
if ( grooveRect.isEmpty() )
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto shadowNode = static_cast< BoxShadowNode* >( node );
|
||||||
|
if ( shadowNode == nullptr )
|
||||||
|
shadowNode = new BoxShadowNode();
|
||||||
|
|
||||||
|
const auto& shadowMetrics = display->shadow();
|
||||||
|
|
||||||
|
shadowNode->setRect( shadowMetrics.shadowRect( grooveRect ) );
|
||||||
|
shadowNode->setShape( grooveRect.width() / 2 );
|
||||||
|
shadowNode->setBlurRadius( shadowMetrics.blurRadius() );
|
||||||
|
shadowNode->setColor( display->shadowColor() );
|
||||||
|
shadowNode->setClipRect( grooveRect );
|
||||||
|
|
||||||
|
shadowNode->updateGeometry();
|
||||||
|
|
||||||
|
return shadowNode;
|
||||||
|
}
|
||||||
|
case ColdAndWarmArcRole:
|
||||||
|
{
|
||||||
|
return updateArcNode( skinnable, node, LightDisplay::ColdAndWarmArc );
|
||||||
|
}
|
||||||
|
case TickmarksRole:
|
||||||
|
{
|
||||||
|
auto ticksNode = static_cast< RadialTickmarksNode* >( node );
|
||||||
|
if ( ticksNode == nullptr )
|
||||||
|
ticksNode = new RadialTickmarksNode();
|
||||||
|
|
||||||
|
QColor color = display->color( LightDisplay::Tickmarks );
|
||||||
|
QRectF ticksRect = display->subControlRect( LightDisplay::Tickmarks );
|
||||||
|
QskArcMetrics arcMetrics = display->arcMetricsHint( LightDisplay::Tickmarks );
|
||||||
|
QskIntervalF boundaries = display->boundaries();
|
||||||
|
QskScaleTickmarks tickmarks;
|
||||||
|
tickmarks.setMajorTicks( {0, 22.5, 45, 67.5, 90, 112.5, 135, 157.5, 180 } );
|
||||||
|
int tickLineWidth = display->metric( LightDisplay::Tickmarks );
|
||||||
|
|
||||||
|
ticksNode->update( color, ticksRect, arcMetrics, boundaries,
|
||||||
|
tickmarks, tickLineWidth, Qt::Horizontal );
|
||||||
|
|
||||||
|
return ticksNode;
|
||||||
|
}
|
||||||
|
case ValueTextRole:
|
||||||
|
{
|
||||||
|
const QString valueText = QString::number( display->value(), 'f', 0 )
|
||||||
|
+ QStringLiteral( " %" );
|
||||||
|
return updateTextNode( skinnable, node, valueText, {},
|
||||||
|
LightDisplay::ValueText );
|
||||||
|
}
|
||||||
|
case LeftLabelRole:
|
||||||
|
{
|
||||||
|
return updateTextNode( skinnable, node, QStringLiteral( "0 " ), {},
|
||||||
|
LightDisplay::LeftLabel );
|
||||||
|
}
|
||||||
|
case RightLabelRole:
|
||||||
|
{
|
||||||
|
return updateTextNode( skinnable, node, QStringLiteral( " 100" ), {},
|
||||||
|
LightDisplay::RightLabel );
|
||||||
|
}
|
||||||
|
case KnobRole:
|
||||||
|
{
|
||||||
|
return updateBoxNode( skinnable, node, LightDisplay::Knob );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
46
examples/iotdashboard/LightDisplaySkinlet.h
Normal file
46
examples/iotdashboard/LightDisplaySkinlet.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Copyright (C) 2021 Edelhirsch Software GmbH
|
||||||
|
* This file may be used under the terms of the 3-clause BSD License
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QskSkinlet.h>
|
||||||
|
|
||||||
|
class LightDisplay;
|
||||||
|
|
||||||
|
class LightDisplaySkinlet : public QskSkinlet
|
||||||
|
{
|
||||||
|
Q_GADGET
|
||||||
|
|
||||||
|
using Inherited = QskSkinlet;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum NodeRole
|
||||||
|
{
|
||||||
|
PanelRole,
|
||||||
|
GrooveRole,
|
||||||
|
ColdAndWarmArcRole,
|
||||||
|
WarmPartRole,
|
||||||
|
TickmarksRole,
|
||||||
|
ValueTextRole,
|
||||||
|
LeftLabelRole,
|
||||||
|
RightLabelRole,
|
||||||
|
KnobRole,
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
@ -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 <QskAnimator.h>
|
|
||||||
#include <QskRgbValue.h>
|
|
||||||
#include <QskSetup.h>
|
|
||||||
#include <QskTextLabel.h>
|
|
||||||
#include <QskQuick.h>
|
|
||||||
|
|
||||||
#include <QGuiApplication>
|
|
||||||
#include <QQuickWindow>
|
|
||||||
#include <QQuickPaintedItem>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QRadialGradient>
|
|
||||||
|
|
||||||
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"
|
|
@ -1,26 +0,0 @@
|
|||||||
/******************************************************************************
|
|
||||||
* Copyright (C) 2021 Edelhirsch Software GmbH
|
|
||||||
* This file may be used under the terms of the 3-clause BSD License
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QskLinearBox.h>
|
|
||||||
|
|
||||||
class QskTextLabel;
|
|
||||||
|
|
||||||
class LightDisplay : public QskLinearBox
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
QSK_SUBCONTROLS( Panel, ColdPart, WarmPart, ValueText )
|
|
||||||
|
|
||||||
LightDisplay( QQuickItem* parent = nullptr );
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void updateLayout() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QskTextLabel* m_valueLabel;
|
|
||||||
};
|
|
@ -8,7 +8,7 @@
|
|||||||
#include "Box.h"
|
#include "Box.h"
|
||||||
#include "BoxWithButtons.h"
|
#include "BoxWithButtons.h"
|
||||||
#include "UsageDiagram.h"
|
#include "UsageDiagram.h"
|
||||||
#include "LightIntensity.h"
|
#include "LightDisplay.h"
|
||||||
#include "MyDevices.h"
|
#include "MyDevices.h"
|
||||||
#include "PieChart.h"
|
#include "PieChart.h"
|
||||||
#include "TopBar.h"
|
#include "TopBar.h"
|
||||||
@ -54,7 +54,9 @@ namespace
|
|||||||
LightIntensity( QQuickItem* parent = nullptr )
|
LightIntensity( QQuickItem* parent = nullptr )
|
||||||
: Box( "Light intensity", parent )
|
: Box( "Light intensity", parent )
|
||||||
{
|
{
|
||||||
new LightDisplay( this );
|
addSpacer( 5 );
|
||||||
|
auto* lightDisplay = new LightDisplay( this );
|
||||||
|
lightDisplay->setValue( 50.0 );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
#include "CircularProgressBarSkinlet.h"
|
#include "CircularProgressBarSkinlet.h"
|
||||||
#include "Diagram.h"
|
#include "Diagram.h"
|
||||||
#include "DiagramSkinlet.h"
|
#include "DiagramSkinlet.h"
|
||||||
#include "LightIntensity.h"
|
#include "LightDisplay.h"
|
||||||
|
#include "LightDisplaySkinlet.h"
|
||||||
#include "MainContent.h"
|
#include "MainContent.h"
|
||||||
#include "MenuBar.h"
|
#include "MenuBar.h"
|
||||||
#include "PieChartPainted.h"
|
#include "PieChartPainted.h"
|
||||||
@ -54,6 +55,7 @@ Skin::Skin( const Palette& palette, QObject* parent )
|
|||||||
{
|
{
|
||||||
declareSkinlet< CircularProgressBar, CircularProgressBarSkinlet >();
|
declareSkinlet< CircularProgressBar, CircularProgressBarSkinlet >();
|
||||||
declareSkinlet< Diagram, DiagramSkinlet >();
|
declareSkinlet< Diagram, DiagramSkinlet >();
|
||||||
|
declareSkinlet< LightDisplay, LightDisplaySkinlet >();
|
||||||
|
|
||||||
initHints( palette );
|
initHints( palette );
|
||||||
}
|
}
|
||||||
@ -180,18 +182,36 @@ void Skin::initHints( const Palette& palette )
|
|||||||
ed.setColor( Diagram::ChartArea3, "#66ff7d34" );
|
ed.setColor( Diagram::ChartArea3, "#66ff7d34" );
|
||||||
|
|
||||||
// light intensity:
|
// light intensity:
|
||||||
ed.setGradient( LightDisplay::ColdPart, { Qt::Horizontal, "#a7b0ff", "#6776ff" } );
|
ed.setBoxShape( LightDisplay::Panel, 100, Qt::RelativeSize );
|
||||||
ed.setGradient( LightDisplay::WarmPart, { Qt::Horizontal, "#feeeb7", "#ff3122" } );
|
|
||||||
|
ed.setArcMetrics( LightDisplay::ColdAndWarmArc, 8.785, 0, 180 * 16 );
|
||||||
|
QskGradient coldGradient( Qt::Horizontal, { { 0.0, 0xffff3122 },
|
||||||
|
{ 0.2, 0xfffeeeb7 },
|
||||||
|
{ 0.3, 0xffa7b0ff },
|
||||||
|
{ 0.5, 0xff6776ff },
|
||||||
|
{ 1.0, Qt::black } } );
|
||||||
|
ed.setGradient( LightDisplay::ColdAndWarmArc, coldGradient );
|
||||||
|
|
||||||
|
ed.setMetric( LightDisplay::Tickmarks, 1 );
|
||||||
|
ed.setArcMetrics( LightDisplay::Tickmarks, { 4.69, 0, 180 * 16 } );
|
||||||
|
ed.setColor( LightDisplay::Tickmarks, 0x55929CB2 );
|
||||||
|
|
||||||
ed.setFontRole( LightDisplay::ValueText, QskSkin::LargeFont );
|
ed.setFontRole( LightDisplay::ValueText, QskSkin::LargeFont );
|
||||||
ed.setColor( LightDisplay::ValueText, "#929cb2" );
|
ed.setColor( LightDisplay::ValueText, "#929cb2" );
|
||||||
|
|
||||||
|
ed.setStrutSize( LightDisplay::Knob, { 20, 20 } );
|
||||||
|
ed.setBoxBorderMetrics( LightDisplay::Knob, 1 );
|
||||||
|
ed.setBoxBorderColors( LightDisplay::Knob, 0xffc4c4c4 );
|
||||||
|
ed.setBoxShape( LightDisplay::Knob, 100, Qt::RelativeSize );
|
||||||
|
|
||||||
// palette dependent skin hints:
|
// palette dependent skin hints:
|
||||||
ed.setGradient( MenuBar::Panel, palette.menuBar );
|
ed.setGradient( MenuBar::Panel, palette.menuBar );
|
||||||
ed.setGradient( MainContent::Panel, palette.mainContent );
|
ed.setGradient( MainContent::Panel, palette.mainContent );
|
||||||
ed.setGradient( Box::Panel, palette.box );
|
ed.setGradient( Box::Panel, palette.box );
|
||||||
ed.setGradient( BoxWithButtons::Panel, palette.box );
|
ed.setGradient( BoxWithButtons::Panel, palette.box );
|
||||||
ed.setGradient( UsageDiagramBox::Panel, palette.box );
|
ed.setGradient( UsageDiagramBox::Panel, palette.box );
|
||||||
ed.setColor( LightDisplay::Panel, palette.lightDisplay );
|
ed.setGradient( LightDisplay::Panel, palette.box );
|
||||||
|
ed.setGradient( LightDisplay::Knob, palette.box );
|
||||||
ed.setGradient( RoundButton::Panel, palette.roundButton );
|
ed.setGradient( RoundButton::Panel, palette.roundButton );
|
||||||
ed.setBoxBorderColors( UsageDiagramBox::DaysBox, palette.weekdayBox );
|
ed.setBoxBorderColors( UsageDiagramBox::DaysBox, palette.weekdayBox );
|
||||||
ed.setColor( QskTextLabel::Text, palette.text );
|
ed.setColor( QskTextLabel::Text, palette.text );
|
||||||
|
@ -10,7 +10,8 @@ SOURCES += \
|
|||||||
Diagram.cpp \
|
Diagram.cpp \
|
||||||
DiagramSkinlet.cpp \
|
DiagramSkinlet.cpp \
|
||||||
GraphicProvider.cpp \
|
GraphicProvider.cpp \
|
||||||
LightIntensity.cpp \
|
LightDisplaySkinlet.cpp \
|
||||||
|
LightDisplay.cpp \
|
||||||
MainContent.cpp \
|
MainContent.cpp \
|
||||||
MenuBar.cpp \
|
MenuBar.cpp \
|
||||||
MyDevices.cpp \
|
MyDevices.cpp \
|
||||||
@ -28,9 +29,10 @@ SOURCES += \
|
|||||||
main.cpp \
|
main.cpp \
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
nodes/BoxShadowNode.cpp \
|
||||||
nodes/DiagramDataNode.cpp \
|
nodes/DiagramDataNode.cpp \
|
||||||
nodes/DiagramSegmentsNode.cpp \
|
nodes/DiagramSegmentsNode.cpp \
|
||||||
nodes/BoxShadowNode.cpp
|
nodes/RadialTickmarksNode.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
Box.h \
|
Box.h \
|
||||||
@ -40,7 +42,8 @@ HEADERS += \
|
|||||||
Diagram.h \
|
Diagram.h \
|
||||||
DiagramSkinlet.h \
|
DiagramSkinlet.h \
|
||||||
GraphicProvider.h \
|
GraphicProvider.h \
|
||||||
LightIntensity.h \
|
LightDisplaySkinlet.h \
|
||||||
|
LightDisplay.h \
|
||||||
MainContent.h \
|
MainContent.h \
|
||||||
MainWindow.h \
|
MainWindow.h \
|
||||||
MenuBar.h \
|
MenuBar.h \
|
||||||
@ -57,9 +60,10 @@ HEADERS += \
|
|||||||
UsageDiagram.h
|
UsageDiagram.h
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
|
nodes/BoxShadowNode.h \
|
||||||
nodes/DiagramDataNode.h \
|
nodes/DiagramDataNode.h \
|
||||||
nodes/DiagramSegmentsNode.h \
|
nodes/DiagramSegmentsNode.h \
|
||||||
nodes/BoxShadowNode.h
|
nodes/RadialTickmarksNode.h
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
images.qrc \
|
images.qrc \
|
||||||
|
129
examples/iotdashboard/nodes/RadialTickmarksNode.cpp
Normal file
129
examples/iotdashboard/nodes/RadialTickmarksNode.cpp
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||||
|
* This file may be used under the terms of the 3-clause BSD License
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "RadialTickmarksNode.h"
|
||||||
|
|
||||||
|
#include <QSGFlatColorMaterial>
|
||||||
|
#include <QtMath>
|
||||||
|
|
||||||
|
QSK_QT_PRIVATE_BEGIN
|
||||||
|
#include <private/qsgnode_p.h>
|
||||||
|
QSK_QT_PRIVATE_END
|
||||||
|
|
||||||
|
static constexpr inline qreal qskTickFactor( QskScaleTickmarks::TickType type )
|
||||||
|
{
|
||||||
|
using TM = QskScaleTickmarks;
|
||||||
|
return type == TM::MinorTick ? 0.7 : ( type == TM::MinorTick ? 0.85 : 1.0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
class RadialTickmarksNodePrivate final : public QSGGeometryNodePrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RadialTickmarksNodePrivate()
|
||||||
|
: geometry( QSGGeometry::defaultAttributes_Point2D(), 0 )
|
||||||
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK( 5, 8, 0 )
|
||||||
|
geometry.setDrawingMode( QSGGeometry::DrawLines );
|
||||||
|
#else
|
||||||
|
geometry.setDrawingMode( GL_LINES );
|
||||||
|
#endif
|
||||||
|
geometry.setVertexDataPattern( QSGGeometry::StaticPattern );
|
||||||
|
}
|
||||||
|
|
||||||
|
QSGGeometry geometry;
|
||||||
|
QSGFlatColorMaterial material;
|
||||||
|
|
||||||
|
QskIntervalF boundaries;
|
||||||
|
QskScaleTickmarks tickmarks;
|
||||||
|
|
||||||
|
QRectF rect;
|
||||||
|
int lineWidth = 0;
|
||||||
|
|
||||||
|
uint hash = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
RadialTickmarksNode::RadialTickmarksNode()
|
||||||
|
: QSGGeometryNode( *new RadialTickmarksNodePrivate )
|
||||||
|
{
|
||||||
|
Q_D( RadialTickmarksNode );
|
||||||
|
|
||||||
|
setGeometry( &d->geometry );
|
||||||
|
setMaterial( &d->material );
|
||||||
|
}
|
||||||
|
|
||||||
|
RadialTickmarksNode::~RadialTickmarksNode()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RadialTickmarksNode::update(const QColor& color, const QRectF& rect,
|
||||||
|
const QskArcMetrics& arcMetrics, const QskIntervalF& /*boundaries*/,
|
||||||
|
const QskScaleTickmarks& tickmarks, int lineWidth,
|
||||||
|
Qt::Orientation /*orientation*/ )
|
||||||
|
{
|
||||||
|
Q_D( RadialTickmarksNode );
|
||||||
|
|
||||||
|
if( lineWidth != d->lineWidth )
|
||||||
|
{
|
||||||
|
d->lineWidth = lineWidth;
|
||||||
|
d->geometry.setLineWidth( lineWidth );
|
||||||
|
|
||||||
|
markDirty( QSGNode::DirtyGeometry );
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint hash = tickmarks.hash( 17435 );
|
||||||
|
|
||||||
|
if( ( hash != d->hash ) || ( rect != d->rect ) )
|
||||||
|
{
|
||||||
|
d->hash = hash;
|
||||||
|
d->rect = rect;
|
||||||
|
|
||||||
|
d->geometry.allocate( tickmarks.tickCount() * 2 );
|
||||||
|
auto vertexData = d->geometry.vertexDataAsPoint2D();
|
||||||
|
|
||||||
|
const auto center = rect.center();
|
||||||
|
const auto radius = 0.5 * rect.width();
|
||||||
|
const auto needleRadius = radius - arcMetrics.width();
|
||||||
|
|
||||||
|
using TM = QskScaleTickmarks;
|
||||||
|
|
||||||
|
for( int i = TM::MinorTick; i <= TM::MajorTick; i++ )
|
||||||
|
{
|
||||||
|
const auto tickType = static_cast< TM::TickType >( i );
|
||||||
|
const auto ticks = tickmarks.ticks( tickType );
|
||||||
|
|
||||||
|
const auto startAngle = arcMetrics.startAngle();
|
||||||
|
const auto endAngle = startAngle + arcMetrics.spanAngle();
|
||||||
|
|
||||||
|
for( const auto tick : ticks )
|
||||||
|
{
|
||||||
|
const qreal ratio = ( tick - startAngle ) / ( endAngle - startAngle );
|
||||||
|
const qreal angle = ratio * ( endAngle - startAngle );
|
||||||
|
|
||||||
|
const qreal cos = qFastCos( qDegreesToRadians( angle ) );
|
||||||
|
const qreal sin = qFastSin( qDegreesToRadians( angle ) );
|
||||||
|
|
||||||
|
const auto xStart = center.x() - radius * cos;
|
||||||
|
const auto yStart = center.y() - radius * sin;
|
||||||
|
|
||||||
|
const auto xEnd = center.x() - needleRadius * cos;
|
||||||
|
const auto yEnd = center.y() - needleRadius * sin;
|
||||||
|
|
||||||
|
vertexData[ 0 ].set( xStart, yStart );
|
||||||
|
vertexData[ 1 ].set( xEnd, yEnd );
|
||||||
|
|
||||||
|
vertexData += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d->geometry.markVertexDataDirty();
|
||||||
|
markDirty( QSGNode::DirtyGeometry );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( color != d->material.color() )
|
||||||
|
{
|
||||||
|
d->material.setColor( color );
|
||||||
|
markDirty( QSGNode::DirtyMaterial );
|
||||||
|
}
|
||||||
|
}
|
27
examples/iotdashboard/nodes/RadialTickmarksNode.h
Normal file
27
examples/iotdashboard/nodes/RadialTickmarksNode.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||||
|
* This file may be used under the terms of the 3-clause BSD License
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QskArcMetrics.h>
|
||||||
|
#include <QskIntervalF.h>
|
||||||
|
#include <QskScaleTickmarks.h>
|
||||||
|
|
||||||
|
#include <QSGGeometryNode>
|
||||||
|
|
||||||
|
class RadialTickmarksNodePrivate;
|
||||||
|
|
||||||
|
class RadialTickmarksNode : public QSGGeometryNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RadialTickmarksNode();
|
||||||
|
~RadialTickmarksNode() override;
|
||||||
|
|
||||||
|
void update( const QColor&, const QRectF&, const QskArcMetrics&,
|
||||||
|
const QskIntervalF&, const QskScaleTickmarks&, int, Qt::Orientation );
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DECLARE_PRIVATE( RadialTickmarksNode )
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user