diff --git a/examples/automotive/SkinFactory.cpp b/examples/automotive/SkinFactory.cpp index b438d6d6..0e0d359f 100644 --- a/examples/automotive/SkinFactory.cpp +++ b/examples/automotive/SkinFactory.cpp @@ -64,23 +64,25 @@ namespace using Q = Speedometer; setBoxBorderMetrics( Q::Panel, 5 ); + setBoxShape( Q::Panel, 30, Qt::RelativeSize ); setGradient( Q::Panel, QskGradient( QskGradient::Vertical, color2, color4 ) ); setBoxBorderColors( Q::Panel, color3 ); - setBoxBorderMetrics( Q::NeedleHead, 5 ); - setMetric( Q::NeedleHead | Size, 10 ); - setGradient( Q::NeedleHead, color2 ); - setBoxBorderColors( Q::NeedleHead, color4 ); + setBoxBorderMetrics( Q::Knob, 5 ); + setStrutSize( Q::Knob, 20, 20 ); + setBoxShape( Q::Knob, 100, Qt::RelativeSize ); + setGradient( Q::Knob, color2 ); + setBoxBorderColors( Q::Knob, color4 ); setMetric( Q::Needle | Size, 4 ); setMetric( Q::Needle | Margin, 15 ); setColor( Q::Needle, color4 ); - setSpacing( Q::Labels, 3 ); - setStrutSize( Q::Labels, 3, 25 ); - setColor( Q::Labels, color4 ); - setFontRole( Q::Labels, QskSkin::SmallFont ); + setSpacing( Q::TickLabels, 3 ); + setStrutSize( Q::TickLabels, 3, 25 ); + setColor( Q::TickLabels, color4 ); + setFontRole( Q::TickLabels, QskSkin::SmallFont ); } { @@ -113,23 +115,24 @@ namespace using Q = Speedometer; setBoxBorderMetrics( Q::Panel, 2 ); + setBoxShape( Q::Panel, 100, Qt::RelativeSize ); setGradient( Q::Panel, color1 ); setBoxBorderColors( Q::Panel, color3 ); - setBoxBorderMetrics( Q::NeedleHead, 2 ); - setMetric( Q::NeedleHead | Size, 15 ); - setGradient( Q::NeedleHead, + setBoxBorderMetrics( Q::Knob, 2 ); + setStrutSize( Q::Knob, 30, 30 ); + setBoxShape( Q::Knob, 100, Qt::RelativeSize ); + setGradient( Q::Knob, QskGradient( QskGradient::Diagonal, color2, color1 ) ); - // setBoxBorderColors( Q::NeedleHead, color4 ); setMetric( Q::Needle | Size, 2 ); setMetric( Q::Needle | Margin, 10 ); setColor( Q::Needle, color2 ); - setSpacing( Q::Labels, 4 ); - setStrutSize( Q::Labels, 2, 15 ); - setColor( Q::Labels, color4 ); - setFontRole( Q::Labels, QskSkin::SmallFont ); + setSpacing( Q::TickLabels, 4 ); + setStrutSize( Q::TickLabels, 2, 15 ); + setColor( Q::TickLabels, color4 ); + setFontRole( Q::TickLabels, QskSkin::SmallFont ); } } }; diff --git a/examples/automotive/Speedometer.cpp b/examples/automotive/Speedometer.cpp index 503925fd..4e8b319c 100644 --- a/examples/automotive/Speedometer.cpp +++ b/examples/automotive/Speedometer.cpp @@ -9,8 +9,8 @@ #include QSK_SUBCONTROL( Speedometer, Panel ) -QSK_SUBCONTROL( Speedometer, Labels ) -QSK_SUBCONTROL( Speedometer, NeedleHead ) +QSK_SUBCONTROL( Speedometer, TickLabels ) +QSK_SUBCONTROL( Speedometer, Knob ) QSK_SUBCONTROL( Speedometer, Needle ) Speedometer::Speedometer( QQuickItem* parent ) @@ -18,14 +18,14 @@ Speedometer::Speedometer( QQuickItem* parent ) { } -QVector< QString > Speedometer::labels() const +QVector< QString > Speedometer::tickLabels() const { - return m_labels; + return m_tickLabels; } -void Speedometer::setLabels( const QVector< QString >& labels ) +void Speedometer::setTickLabels( const QVector< QString >& labels ) { - m_labels = labels; + m_tickLabels = labels; } #include "moc_Speedometer.cpp" diff --git a/examples/automotive/Speedometer.h b/examples/automotive/Speedometer.h index dad68363..edfa2d0f 100644 --- a/examples/automotive/Speedometer.h +++ b/examples/automotive/Speedometer.h @@ -13,15 +13,15 @@ class Speedometer : public QskBoundedValueInput Q_OBJECT public: - QSK_SUBCONTROLS( Panel, Labels, NeedleHead, Needle ) + QSK_SUBCONTROLS( Panel, TickLabels, Knob, Needle ) Speedometer( QQuickItem* parent = nullptr ); - QVector< QString > labels() const; - void setLabels( const QVector< QString >& ); + QVector< QString > tickLabels() const; + void setTickLabels( const QVector< QString >& ); private: - QVector< QString > m_labels; + QVector< QString > m_tickLabels; }; #endif diff --git a/examples/automotive/SpeedometerDisplay.cpp b/examples/automotive/SpeedometerDisplay.cpp index 4f9086d9..1cac6072 100644 --- a/examples/automotive/SpeedometerDisplay.cpp +++ b/examples/automotive/SpeedometerDisplay.cpp @@ -60,7 +60,7 @@ namespace for ( int i = 0; i < labelsCount; i++ ) labels += QString::number( i ); - setLabels( labels ); + setTickLabels( labels ); } }; @@ -74,7 +74,7 @@ namespace setMaximum( 35 ); setValue( -90 ); - constexpr int labelsCount = 23; + constexpr int labelsCount = 17; QVector< QString > labels; labels.reserve( labelsCount ); @@ -82,7 +82,7 @@ namespace for ( int i = 0; i < labelsCount; i++ ) labels.append( QString::number( i * 10 ) ); - setLabels( labels ); + setTickLabels( labels ); } void updateValue() @@ -101,7 +101,7 @@ namespace setMaximum( 345 ); setValue( 330 ); - setLabels( { "0", "", "1/2", "", "1/1" } ); + setTickLabels( { "0", "", "1/2", "", "1/1" } ); } void updateValue() diff --git a/examples/automotive/SpeedometerSkinlet.cpp b/examples/automotive/SpeedometerSkinlet.cpp index 25239c6a..fb8fa81e 100644 --- a/examples/automotive/SpeedometerSkinlet.cpp +++ b/examples/automotive/SpeedometerSkinlet.cpp @@ -16,16 +16,17 @@ #include #include +#include #include #include namespace { - class TicksNode : public QSGGeometryNode + class LinesNode : public QSGGeometryNode { public: - TicksNode() - : m_geometry( QSGGeometry::defaultAttributes_Point2D(), 0 ) + LinesNode( int lineCount = 0 ) + : m_geometry( QSGGeometry::defaultAttributes_Point2D(), 2 * lineCount ) { #if QT_VERSION >= QT_VERSION_CHECK( 5, 8, 0 ) m_geometry.setDrawingMode( QSGGeometry::DrawLines ); @@ -40,29 +41,70 @@ namespace void setColor( const QColor& color ) { - m_material.setColor( color ); + if ( color != m_material.color() ) + { + m_material.setColor( color ); + markDirty( QSGNode::DirtyMaterial ); + } } private: QSGFlatColorMaterial m_material; QSGGeometry m_geometry; }; -} // namespace + + class TicksNode : public LinesNode + { + public: + TicksNode() + { + } + }; + + class NeedleNode : public LinesNode + { + public: + NeedleNode() + : LinesNode( 1 ) + { + } + + void setData( const QLineF& line, qreal width ) + { + auto vertexData = geometry()->vertexDataAsPoint2D(); + vertexData[ 0 ].set( line.x1(), line.y1() ); + vertexData[ 1 ].set( line.x2(), line.y2() ); + + geometry()->setLineWidth( width ); + geometry()->markVertexDataDirty(); + + markDirty( QSGNode::DirtyGeometry ); + } + }; +} SpeedometerSkinlet::SpeedometerSkinlet( QskSkin* skin ) : QskSkinlet( skin ) { - setNodeRoles( { PanelRole, LabelsRole, NeedleRole } ); + setNodeRoles( { PanelRole, NeedleRole, KnobRole, LabelsRole } ); } -SpeedometerSkinlet::~SpeedometerSkinlet() = default; - -QRectF SpeedometerSkinlet::subControlRect( const QskSkinnable*, - const QRectF& contentsRect, QskAspect::Subcontrol ) const +QRectF SpeedometerSkinlet::subControlRect( const QskSkinnable* skinnable, + const QRectF& contentsRect, QskAspect::Subcontrol subcontrol ) const { - const auto extent = qMin( contentsRect.width(), contentsRect.height() ); + QRectF r; - QRectF r( 0.0, 0.0, extent, extent ); + if ( subcontrol == Speedometer::Knob ) + { + const auto size = skinnable->strutSizeHint( Speedometer::Knob ); + r.setSize( size ); + } + else + { + const auto extent = qMin( contentsRect.width(), contentsRect.height() ); + r.setSize( QSizeF( extent, extent ) ); + } + r.moveCenter( contentsRect.center() ); return r; @@ -76,98 +118,65 @@ QSGNode* SpeedometerSkinlet::updateSubNode( switch ( nodeRole ) { case PanelRole: - return updatePanelNode( speedometer, node ); + return updateBoxNode( speedometer, node, Speedometer::Panel ); - case LabelsRole: - return updateLabelsNode( speedometer, node ); + case KnobRole: + return updateBoxNode( speedometer, node, Speedometer::Knob ); case NeedleRole: return updateNeedleNode( speedometer, node ); + case LabelsRole: + return updateLabelsNode( speedometer, node ); + + default: return nullptr; } } -QSGNode* SpeedometerSkinlet::updatePanelNode( - const Speedometer* speedometer, QSGNode* node ) const -{ - auto boxNode = static_cast< QskBoxNode* >( node ); - - if ( boxNode == nullptr ) - { - boxNode = new QskBoxNode; - } - - const auto panelRect = subControlRect( - speedometer, speedometer->contentsRect(), Speedometer::Panel ); - - const qreal radius = panelRect.width() / 2; - - QskBoxShapeMetrics shapeMetrics( radius, radius, radius, radius ); - QskBoxBorderMetrics borderMetrics = speedometer->boxBorderMetricsHint( Speedometer::Panel ); - QskBoxBorderColors borderColors = speedometer->boxBorderColorsHint( Speedometer::Panel ); - QskGradient gradient = speedometer->gradientHint( Speedometer::Panel ); - boxNode->setBoxData( panelRect, shapeMetrics, borderMetrics, borderColors, gradient ); - - return boxNode; -} - QSGNode* SpeedometerSkinlet::updateLabelsNode( const Speedometer* speedometer, QSGNode* node ) const { using Q = Speedometer; - const int labelsCount = speedometer->labels().count(); + const auto labels = speedometer->tickLabels(); // ### actually, we could draw labels with only one entry - if ( labelsCount <= 1 ) - { + if ( labels.count() <= 1 ) return nullptr; - } auto ticksNode = static_cast< TicksNode* >( node ); - if ( ticksNode == nullptr ) - { ticksNode = new TicksNode(); - } - const auto color = speedometer->color( Q::Labels ); + const auto color = speedometer->color( Q::TickLabels ); ticksNode->setColor( color ); const auto startAngle = speedometer->minimum(); const auto endAngle = speedometer->maximum(); - const auto step = ( endAngle - startAngle ) / ( labelsCount - 1 ); + const auto step = ( endAngle - startAngle ) / ( labels.count() - 1 ); auto geometry = ticksNode->geometry(); - geometry->allocate( labelsCount * 2 ); + geometry->allocate( labels.count() * 2 ); auto vertexData = geometry->vertexDataAsPoint2D(); - memset( vertexData, 0, static_cast< size_t >( geometry->vertexCount() ) ); - const auto panelMargins = speedometer->marginHint( Q::Panel ); + auto scaleRect = this->scaleRect( speedometer ); - auto panelRect = subControlRect( - speedometer, speedometer->contentsRect(), Q::Panel ); - panelRect = panelRect.marginsRemoved( panelMargins ); + const auto center = scaleRect.center(); + const auto radius = 0.5 * scaleRect.width(); - QPointF center = QPointF( panelRect.x() + panelRect.width() / 2, - panelRect.y() + panelRect.height() / 2 ); - - const auto radius = static_cast< float >( panelRect.width() / 2 ); - - const auto spacing = speedometer->spacingHint( Q::Labels ); - QFontMetricsF fontMetrics( speedometer->effectiveFont( Q::Labels ) ); + const auto spacing = speedometer->spacingHint( Q::TickLabels ); + QFontMetricsF fontMetrics( speedometer->effectiveFont( Q::TickLabels ) ); auto angle = startAngle; - const auto labels = speedometer->labels(); - const auto tickSize = speedometer->strutSizeHint( Q::Labels ); + const auto tickSize = speedometer->strutSizeHint( Q::TickLabels ); const auto needleRadius = radius - tickSize.height(); // Create a series of tickmarks from minimum to maximum - for ( int i = 0; i < labelsCount; ++i, angle += step ) + for ( int i = 0; i < labels.count(); ++i, angle += step ) { const qreal cos = qFastCos( qDegreesToRadians( angle ) ); const qreal sin = qFastSin( qDegreesToRadians( angle ) ); @@ -196,7 +205,7 @@ QSGNode* SpeedometerSkinlet::updateLabelsNode( const auto numbersX = xEnd - ( spacing * cos ) + adjustX; const auto numbersY = yEnd - ( spacing * sin ) + adjustY; - QRectF numbersRect( numbersX, numbersY, w, h ); + const QRectF numbersRect( numbersX, numbersY, w, h ); QskTextNode* numbersNode; @@ -209,15 +218,12 @@ QSGNode* SpeedometerSkinlet::updateLabelsNode( numbersNode = new QskTextNode(); } - const auto font = speedometer->effectiveFont( Q::Labels ); + const auto font = speedometer->effectiveFont( Q::TickLabels ); numbersNode->setTextData( speedometer, text, numbersRect, font, QskTextOptions(), QskTextColors( color ), Qt::AlignCenter | Qt::AlignHCenter, Qsk::Normal ); if ( ticksNode->childCount() <= i ) - { ticksNode->appendChildNode( numbersNode ); - } - // ### remove nodes in case they are superfluous } } @@ -232,76 +238,45 @@ QSGNode* SpeedometerSkinlet::updateLabelsNode( QSGNode* SpeedometerSkinlet::updateNeedleNode( const Speedometer* speedometer, QSGNode* node ) const { - using namespace QskAspect; using Q = Speedometer; - auto needleNode = static_cast< TicksNode* >( node ); + auto needleNode = static_cast< NeedleNode* >( node ); if ( needleNode == nullptr ) - needleNode = new TicksNode(); + needleNode = new NeedleNode(); - QskBoxNode* boxNode; + const auto line = needlePoints( speedometer ); + const auto width = speedometer->metric( Q::Needle | QskAspect::Size ); - if ( needleNode->childCount() == 0 ) - { - boxNode = new QskBoxNode(); - needleNode->appendChildNode( boxNode ); - } - else - { - boxNode = static_cast< QskBoxNode* >( needleNode->childAtIndex( 0 ) ); - } - - const auto margins = speedometer->marginHint( Q::Panel ); - - auto panelRect = subControlRect( - speedometer, speedometer->contentsRect(), Q::Panel ); - - panelRect = panelRect.marginsRemoved( margins ); - - const auto radius = speedometer->metric( Q::NeedleHead | Size ); - const QPointF center = panelRect.center(); - - const auto borderMetrics = speedometer->boxBorderMetricsHint( Q::NeedleHead ); - const auto borderColors = speedometer->boxBorderColorsHint( Q::NeedleHead ); - const auto gradient = speedometer->gradientHint( Q::NeedleHead ); - - QRectF centerNodeRect( center.x() - radius, center.y() - radius, 2 * radius, 2 * radius ); - boxNode->setBoxData( centerNodeRect, radius, borderMetrics, borderColors, gradient ); - - QColor color = speedometer->color( Q::Needle ); - needleNode->setColor( color ); - - auto panelRadius = static_cast< float >( panelRect.width() / 2 ); - - const auto needleWidth = speedometer->metric( Q::Needle | Size ); - const auto needleMargin = speedometer->metric( Q::Needle | Margin ); - - const float xStart = center.x(); - const float yStart = center.y(); - - const float angle = speedometer->value(); - const qreal cosine = qCos( qDegreesToRadians( angle ) ); - const qreal sine = qSin( qDegreesToRadians( angle ) ); - - const float needleRadius = panelRadius - needleMargin; - const float xEnd = center.x() + needleRadius * cosine; - const float yEnd = center.y() + needleRadius * sine; - - auto geometry = needleNode->geometry(); - geometry->allocate( 2 ); - - auto vertexData = geometry->vertexDataAsPoint2D(); - memset( vertexData, 0, static_cast< size_t >( geometry->vertexCount() ) ); - - vertexData[ 0 ].set( xStart, yStart ); - vertexData[ 1 ].set( xEnd, yEnd ); - - geometry->setLineWidth( 2 * needleWidth ); - geometry->markVertexDataDirty(); - - needleNode->markDirty( QSGNode::DirtyGeometry ); + needleNode->setData( line, width * 2 ); + needleNode->setColor( speedometer->color( Q::Needle ) ); return needleNode; } +QRectF SpeedometerSkinlet::scaleRect( const Speedometer* speedometer ) const +{ + using Q = Speedometer; + + const auto margins = speedometer->marginHint( Q::Panel ); + + auto r = speedometer->subControlRect( Q::Panel ); + r = r.marginsRemoved( margins ); + + return r; +} + +QLineF SpeedometerSkinlet::needlePoints( const Speedometer* speedometer ) const +{ + const auto r = scaleRect( speedometer ); + const auto margin = speedometer->metric( + Speedometer::Needle | QskAspect::Margin ); + + QLineF line; + line.setP1( r.center() ); + line.setLength( 0.5 * r.width() - margin ); + line.setAngle( -speedometer->value() ); + + return line; +} + #include "moc_SpeedometerSkinlet.cpp" diff --git a/examples/automotive/SpeedometerSkinlet.h b/examples/automotive/SpeedometerSkinlet.h index ed84f2c1..897a3fd4 100644 --- a/examples/automotive/SpeedometerSkinlet.h +++ b/examples/automotive/SpeedometerSkinlet.h @@ -19,11 +19,11 @@ class SpeedometerSkinlet : public QskSkinlet { PanelRole, LabelsRole, + KnobRole, NeedleRole }; Q_INVOKABLE SpeedometerSkinlet( QskSkin* skin = nullptr ); - ~SpeedometerSkinlet() override; QRectF subControlRect( const QskSkinnable*, const QRectF&, QskAspect::Subcontrol ) const override; @@ -33,7 +33,9 @@ class SpeedometerSkinlet : public QskSkinlet quint8 nodeRole, QSGNode* node ) const override; private: - QSGNode* updatePanelNode( const Speedometer*, QSGNode* ) const; + QRectF scaleRect( const Speedometer* ) const; + QLineF needlePoints( const Speedometer* ) const; + QSGNode* updateLabelsNode( const Speedometer*, QSGNode* ) const; QSGNode* updateNeedleNode( const Speedometer*, QSGNode* ) const; }; diff --git a/examples/automotive/main.cpp b/examples/automotive/main.cpp index 508cbec7..8648e686 100644 --- a/examples/automotive/main.cpp +++ b/examples/automotive/main.cpp @@ -11,12 +11,17 @@ #include #include -#include +#include +#include #include int main( int argc, char** argv ) { +#ifdef ITEM_STATISTICS + QskObjectCounter counter( true ); +#endif + qskSkinManager->setPluginPaths( QStringList() ); // no skin plugins qskSkinManager->registerFactory( QStringLiteral( "sample" ), new SkinFactory() );