Speedometer: inherit from QskRangeControl and make skinnable (#62)

* Speedometer: Inherit from QskRangeControl

We don't need start/endAngle() etc. and value() anymore,
but can use the methods from QskRangeControl.

* Speedometer: Make it skinnable

* automotive example: Only use two specially created skins

... because the default skins do not have hints for e.g. the speedometer.

Also, display the speedometer page by default.
This commit is contained in:
Peter Hartmann 2018-05-09 08:31:43 +02:00 committed by uwerat
parent 6b47678805
commit 60a4f2ff1f
8 changed files with 118 additions and 106 deletions

View File

@ -74,7 +74,7 @@ namespace {
return font; return font;
} }
} } // namespace
DefaultSkin::DefaultSkin( const QString& name, QObject* parent ): DefaultSkin::DefaultSkin( const QString& name, QObject* parent ):
QskSkin( parent ), QskSkin( parent ),
@ -101,7 +101,8 @@ void DefaultSkin::initHints()
setColor( QskTextLabel::Text, m_palette->color4 ); setColor( QskTextLabel::Text, m_palette->color4 );
// - sound control // -- sound control
setGradient( SoundControl::Overlay, 0 ); setGradient( SoundControl::Overlay, 0 );
setGradient( SoundControl::CrossHair, m_palette->color3 ); setGradient( SoundControl::CrossHair, m_palette->color3 );
setGradient( SoundControl::Marker, m_palette->color5 ); setGradient( SoundControl::Marker, m_palette->color5 );
@ -146,6 +147,30 @@ void DefaultSkin::initHints()
setBoxShape( QskSlider::Handle, 100.0, Qt::RelativeSize ); setBoxShape( QskSlider::Handle, 100.0, Qt::RelativeSize );
setGradient( QskSlider::Handle, m_palette->color5 ); setGradient( QskSlider::Handle, m_palette->color5 );
// -- speedometers
setBoxBorderMetrics( Speedometer::Panel, 2 );
setGradient( Speedometer::Panel, m_palette->color1 );
setBoxBorderColors( Speedometer::Panel, m_palette->color3 );
setBoxBorderMetrics( Speedometer::NeedleHead, 2 );
setMetric( Speedometer::NeedleHead | QskAspect::Size, 15 );
setGradient( Speedometer::NeedleHead, QskGradient( QskGradient::Diagonal,
m_palette->color2, m_palette->color1 ) );
// setBoxBorderColors( Speedometer::NeedleHead, m_palette->color4 );
setMetric( Speedometer::Needle | QskAspect::MinimumWidth, 2 );
setMetric( Speedometer::Needle | QskAspect::Margin, 10 );
setColor( Speedometer::Needle, m_palette->color2 );
// margins between numbers and ticks:
setMargins( Speedometer::Labels, QskMargins( 4, 4, 4, 4 ) );
setMetric( Speedometer::Labels | QskAspect::MinimumWidth, 2 );
setMetric( Speedometer::Labels | QskAspect::Size, 15 ); // ticks size
setColor( Speedometer::Labels, m_palette->color4 );
setFontRole( Speedometer::Labels, QskSkin::SmallFont );
// handle expanding, when being pressed // handle expanding, when being pressed
for ( auto state : { QskAspect::NoState, QskSlider::Pressed } ) for ( auto state : { QskAspect::NoState, QskSlider::Pressed } )
{ {

View File

@ -13,7 +13,7 @@
#include <QDate> #include <QDate>
#include <QImage> #include <QImage>
#define SPEEDO 0 #define SPEEDO 1
MainWindow::MainWindow() MainWindow::MainWindow()
{ {

View File

@ -140,6 +140,29 @@ void OtherSkin::initHints()
setBoxBorderMetrics( QskSlider::Handle, 0 ); setBoxBorderMetrics( QskSlider::Handle, 0 );
setBoxShape( QskSlider::Handle, 6 ); setBoxShape( QskSlider::Handle, 6 );
setGradient( QskSlider::Handle, m_palette->color3 ); setGradient( QskSlider::Handle, m_palette->color3 );
// -- speedometers
setBoxBorderMetrics( Speedometer::Panel, 5 );
setGradient( Speedometer::Panel, QskGradient( QskGradient::Vertical,
m_palette->color2, m_palette->color4 ) );
setBoxBorderColors( Speedometer::Panel, m_palette->color3 );
setBoxBorderMetrics( Speedometer::NeedleHead, 5 );
setMetric( Speedometer::NeedleHead | QskAspect::Size, 10 );
setGradient( Speedometer::NeedleHead, m_palette->color2 );
setBoxBorderColors( Speedometer::NeedleHead, m_palette->color4 );
setMetric( Speedometer::Needle | QskAspect::MinimumWidth, 4 );
setMetric( Speedometer::Needle | QskAspect::Margin, 15 );
setColor( Speedometer::Needle, m_palette->color4 );
// margins between numbers and ticks:
setMargins( Speedometer::Labels, QskMargins( 3, 3, 3, 3 ) );
setMetric( Speedometer::Labels | QskAspect::MinimumWidth, 3 );
setMetric( Speedometer::Labels | QskAspect::Size, 25 ); // ticks size
setColor( Speedometer::Labels, m_palette->color4 );
setFontRole( Speedometer::Labels, QskSkin::SmallFont );
} }
void OtherSkin::initGraphicFilters() void OtherSkin::initGraphicFilters()

View File

@ -16,7 +16,7 @@
QStringList SkinFactory::skinNames() const QStringList SkinFactory::skinNames() const
{ {
return { "DefaultSkin", "OtherSkin", "SquiekSkin", "MaterialSkin" }; return { "DefaultSkin", "OtherSkin" };
} }
QskSkin* SkinFactory::createSkin( const QString& skinName ) QskSkin* SkinFactory::createSkin( const QString& skinName )

View File

@ -5,47 +5,14 @@
QSK_SUBCONTROL( Speedometer, Panel ) QSK_SUBCONTROL( Speedometer, Panel )
QSK_SUBCONTROL( Speedometer, Labels ) QSK_SUBCONTROL( Speedometer, Labels )
QSK_SUBCONTROL( Speedometer, NeedleHead )
QSK_SUBCONTROL( Speedometer, Needle ) QSK_SUBCONTROL( Speedometer, Needle )
Speedometer::Speedometer( QQuickItem* parent ) : Speedometer::Speedometer( QQuickItem* parent ) :
QskControl( parent ), QskRangeControl( parent )
m_value( 0.0 ),
m_startAngle( -215 ),
m_endAngle( 35 )
{ {
} }
float Speedometer::value() const
{
return m_value;
}
void Speedometer::setValue( float value )
{
m_value = value;
update();
}
float Speedometer::startAngle() const
{
return m_startAngle;
}
void Speedometer::setStartAngle( float startAngle )
{
m_startAngle = startAngle;
}
float Speedometer::endAngle() const
{
return m_endAngle;
}
void Speedometer::setEndAngle( float endAngle )
{
m_endAngle = endAngle;
}
QVector< QString > Speedometer::labels() const QVector< QString > Speedometer::labels() const
{ {
return m_labels; return m_labels;

View File

@ -1,33 +1,21 @@
#ifndef SPEEDOMETER_H #ifndef SPEEDOMETER_H
#define SPEEDOMETER_H #define SPEEDOMETER_H
#include <QskControl.h> #include <QskRangeControl.h>
class Speedometer : public QskControl class Speedometer : public QskRangeControl
{ {
Q_OBJECT Q_OBJECT
public: public:
QSK_SUBCONTROLS( Panel, Labels, Needle ) QSK_SUBCONTROLS( Panel, Labels, NeedleHead, Needle )
Speedometer( QQuickItem* parent = nullptr ); Speedometer( QQuickItem* parent = nullptr );
float value() const;
void setValue( float value ); // angle; should be within a set range
float startAngle() const;
void setStartAngle( float startAngle );
float endAngle() const;
void setEndAngle( float endAngle );
QVector< QString > labels() const; QVector< QString > labels() const;
void setLabels( const QVector< QString >& labels ); void setLabels( const QVector< QString >& labels );
private: private:
float m_value;
float m_startAngle;
float m_endAngle;
float m_labelsStep; float m_labelsStep;
QVector< QString > m_labels; QVector< QString > m_labels;
}; };

View File

@ -29,11 +29,12 @@ SpeedometerDisplay::SpeedometerDisplay( QQuickItem *parent ) :
m_box->setSpacing( 20 ); m_box->setSpacing( 20 );
m_revCounter->setObjectName( QStringLiteral( "RevCounter" ) ); m_revCounter->setObjectName( QStringLiteral( "RevCounter" ) );
int startAngle = 145, endAngle = 305, value = 200, numberLabels = 8; m_revCounter->setMinimum( 145 );
m_revCounter->setStartAngle( startAngle ); m_revCounter->setMaximum( 305 );
m_revCounter->setEndAngle( endAngle ); m_revCounter->setValue( 200 );
m_revCounter->setValue( value );
QVector< QString > revCounterLabels; QVector< QString > revCounterLabels;
int numberLabels = 8;
revCounterLabels.reserve( numberLabels );
for ( int i = 0; i < numberLabels; ++i ) for ( int i = 0; i < numberLabels; ++i )
{ {
@ -43,14 +44,11 @@ SpeedometerDisplay::SpeedometerDisplay( QQuickItem *parent ) :
m_revCounter->setLabels( revCounterLabels ); m_revCounter->setLabels( revCounterLabels );
m_speedometer->setObjectName( QStringLiteral( "Speedometer" ) ); m_speedometer->setObjectName( QStringLiteral( "Speedometer" ) );
value = 280; m_speedometer->setMinimum( -215 );
numberLabels = 23; m_speedometer->setMaximum( 35 );
startAngle = -215; m_speedometer->setValue( -90 );
endAngle = 35;
m_speedometer->setStartAngle( startAngle );
m_speedometer->setEndAngle( endAngle );
m_speedometer->setValue( value );
QVector< QString > speedometerLabels; QVector< QString > speedometerLabels;
numberLabels = 23;
speedometerLabels.reserve( numberLabels ); speedometerLabels.reserve( numberLabels );
for ( int i = 0; i < numberLabels; ++i ) for ( int i = 0; i < numberLabels; ++i )
@ -76,8 +74,8 @@ SpeedometerDisplay::SpeedometerDisplay( QQuickItem *parent ) :
m_fuelGauge->setObjectName( QStringLiteral( "Fuel Gauge" ) ); m_fuelGauge->setObjectName( QStringLiteral( "Fuel Gauge" ) );
m_fuelGauge->setStartAngle( 195 ); m_fuelGauge->setMinimum( 195 );
m_fuelGauge->setEndAngle( 345 ); m_fuelGauge->setMaximum( 345 );
m_fuelGauge->setValue( 330 ); m_fuelGauge->setValue( 330 );
QVector< QString > fuelGaugeLabels; QVector< QString > fuelGaugeLabels;

View File

@ -15,22 +15,24 @@
namespace namespace
{ {
class TicksNode : public QSGGeometryNode class TicksNode : public QSGGeometryNode
{ {
public: public:
TicksNode( const QColor& color ): TicksNode():
m_geometry( QSGGeometry::defaultAttributes_Point2D(), 0 ) m_geometry( QSGGeometry::defaultAttributes_Point2D(), 0 )
{ {
m_geometry.setDrawingMode( GL_LINES ); m_geometry.setDrawingMode( GL_LINES );
m_geometry.setVertexDataPattern( QSGGeometry::StaticPattern ); m_geometry.setVertexDataPattern( QSGGeometry::StaticPattern );
m_material.setColor( color );
setGeometry( &m_geometry ); setGeometry( &m_geometry );
setMaterial( &m_material ); setMaterial( &m_material );
} }
void setColor( const QColor& color )
{
m_material.setColor( color );
}
private: private:
QSGFlatColorMaterial m_material; QSGFlatColorMaterial m_material;
QSGGeometry m_geometry; QSGGeometry m_geometry;
@ -87,9 +89,9 @@ QSGNode* SpeedometerSkinlet::updatePanelNode( const Speedometer* speedometer, QS
QRectF panelRect = subControlRect( speedometer, Speedometer::Panel ); QRectF panelRect = subControlRect( speedometer, Speedometer::Panel );
qreal radius = panelRect.width() / 2; qreal radius = panelRect.width() / 2;
QskBoxShapeMetrics shapeMetrics( radius, radius, radius, radius ); QskBoxShapeMetrics shapeMetrics( radius, radius, radius, radius );
QskBoxBorderMetrics borderMetrics( 2 ); QskBoxBorderMetrics borderMetrics = speedometer->boxBorderMetricsHint( Speedometer::Panel );
QskBoxBorderColors borderColors( Qt::white ); QskBoxBorderColors borderColors = speedometer->boxBorderColorsHint( Speedometer::Panel );
QskGradient gradient( Qt::black ); QskGradient gradient = speedometer->gradientHint( Speedometer::Panel );
boxNode->setBoxData( panelRect, shapeMetrics, borderMetrics, borderColors, gradient ); boxNode->setBoxData( panelRect, shapeMetrics, borderMetrics, borderColors, gradient );
return boxNode; return boxNode;
@ -109,11 +111,14 @@ QSGNode* SpeedometerSkinlet::updateLabelsNode( const Speedometer* speedometer, Q
if ( ticksNode == nullptr ) if ( ticksNode == nullptr )
{ {
ticksNode = new TicksNode( Qt::white ); ticksNode = new TicksNode();
} }
const float startAngle = speedometer->startAngle(); QColor color = speedometer->color( Speedometer::Labels );
const float endAngle = speedometer->endAngle(); 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 ) / ( labelsCount - 1 );
auto geometry = ticksNode->geometry(); auto geometry = ticksNode->geometry();
@ -130,10 +135,12 @@ QSGNode* SpeedometerSkinlet::updateLabelsNode( const Speedometer* speedometer, Q
panelRect.y() + panelRect.height() / 2 ); panelRect.y() + panelRect.height() / 2 );
auto radius = static_cast< float >( panelRect.width() / 2 ); auto radius = static_cast< float >( panelRect.width() / 2 );
const QMarginsF numbersMargins = speedometer->marginsHint( Speedometer::Labels | QskAspect::Margin ); const QMarginsF numbersMargins = speedometer->marginsHint( Speedometer::Labels );
QFontMetrics fontMetrics( speedometer->effectiveFont( Speedometer::Labels ) ); QFontMetrics fontMetrics( speedometer->effectiveFont( Speedometer::Labels ) );
float angle = startAngle; auto angle = startAngle;
qreal length = speedometer->metric( Speedometer::Labels | QskAspect::Size );
QVector< QString > labels = speedometer->labels();
// Create a series of tickmarks from minimum to maximum // Create a series of tickmarks from minimum to maximum
for( int i = 0; i < labelsCount; ++i, angle += step ) for( int i = 0; i < labelsCount; ++i, angle += step )
@ -141,33 +148,30 @@ QSGNode* SpeedometerSkinlet::updateLabelsNode( const Speedometer* speedometer, Q
qreal cosine = qCos( qDegreesToRadians( angle ) ); qreal cosine = qCos( qDegreesToRadians( angle ) );
qreal sine = qSin( qDegreesToRadians( angle ) ); qreal sine = qSin( qDegreesToRadians( angle ) );
float xStart = center.x() + radius * cosine; auto xStart = center.x() + radius * cosine;
float yStart = center.y() + radius * sine; auto yStart = center.y() + radius * sine;
// ### skin hint for each of highlighted / normal marks // ### skin hint for each of highlighted / normal marks
qreal length = 15; auto xEnd = center.x() + ( radius - length ) * cosine;
float xEnd = center.x() + ( radius - length ) * cosine; auto yEnd = center.y() + ( radius - length ) * sine;
float yEnd = center.y() + ( radius - length ) * sine;
vertexData[0].set( xStart, yStart ); vertexData[0].set( xStart, yStart );
vertexData[1].set( xEnd, yEnd ); vertexData[1].set( xEnd, yEnd );
vertexData += 2; vertexData += 2;
QVector< QString > labels = speedometer->labels();
// only create a text node if there is a label for it: // only create a text node if there is a label for it:
if ( labels.count() > i ) if ( labels.count() > i )
{ {
const QString& text = labels.at( i ); const QString& text = labels.at( i );
float w = fontMetrics.width( text ); auto w = fontMetrics.width( text );
float h = fontMetrics.height(); auto h = fontMetrics.height();
float adjustX = ( -0.5 * cosine - 0.5 ) * w; auto adjustX = ( -0.5 * cosine - 0.5 ) * w;
float adjustY = ( -0.5 * sine - 0.5 ) * h; auto adjustY = ( -0.5 * sine - 0.5 ) * h;
float numbersX = xEnd + ( -1 * numbersMargins.left() * cosine ) + adjustX; auto numbersX = xEnd + ( -1 * numbersMargins.left() * cosine ) + adjustX;
float numbersY = yEnd + ( -1 * numbersMargins.top() * sine ) + adjustY; auto numbersY = yEnd + ( -1 * numbersMargins.top() * sine ) + adjustY;
QRectF numbersRect( numbersX, numbersY, w, h ); QRectF numbersRect( numbersX, numbersY, w, h );
@ -182,8 +186,9 @@ QSGNode* SpeedometerSkinlet::updateLabelsNode( const Speedometer* speedometer, Q
numbersNode = new QskTextNode(); numbersNode = new QskTextNode();
} }
numbersNode->setTextData( speedometer, text, numbersRect, QFont(), QFont font = speedometer->effectiveFont( Speedometer::Labels );
QskTextOptions(), QskTextColors( Qt::white ), numbersNode->setTextData( speedometer, text, numbersRect, font,
QskTextOptions(), QskTextColors( color ),
Qt::AlignCenter | Qt::AlignHCenter, Qsk::Normal ); Qt::AlignCenter | Qt::AlignHCenter, Qsk::Normal );
if ( ticksNode->childCount() <= i ) if ( ticksNode->childCount() <= i )
@ -194,7 +199,8 @@ QSGNode* SpeedometerSkinlet::updateLabelsNode( const Speedometer* speedometer, Q
} }
} }
geometry->setLineWidth( 2 ); auto lineWidth = speedometer->metric( Speedometer::Labels | QskAspect::MinimumWidth );
geometry->setLineWidth( lineWidth );
geometry->markVertexDataDirty(); geometry->markVertexDataDirty();
ticksNode->markDirty( QSGNode::DirtyGeometry ); ticksNode->markDirty( QSGNode::DirtyGeometry );
@ -206,7 +212,7 @@ QSGNode* SpeedometerSkinlet::updateNeedleNode( const Speedometer* speedometer, Q
{ {
QMarginsF margins = speedometer->marginsHint( Speedometer::Panel | QskAspect::Margin ); QMarginsF margins = speedometer->marginsHint( Speedometer::Panel | QskAspect::Margin );
const QRectF panelRect = subControlRect( speedometer, Speedometer::Panel ).marginsRemoved( margins ); const QRectF panelRect = subControlRect( speedometer, Speedometer::Panel ).marginsRemoved( margins );
auto radius = 15; // ### skin hint auto radius = speedometer->metric( Speedometer::NeedleHead | QskAspect::Size );
QPointF center = QPointF( panelRect.x() + panelRect.width() / 2, QPointF center = QPointF( panelRect.x() + panelRect.width() / 2,
panelRect.y() + panelRect.height() / 2 ); panelRect.y() + panelRect.height() / 2 );
@ -217,38 +223,43 @@ QSGNode* SpeedometerSkinlet::updateNeedleNode( const Speedometer* speedometer, Q
boxNode = new QskBoxNode; boxNode = new QskBoxNode;
} }
QskBoxShapeMetrics shapeMetrics( radius, radius, radius, radius );
QskBoxBorderMetrics borderMetrics = speedometer->boxBorderMetricsHint( Speedometer::NeedleHead );
QskBoxBorderColors borderColors = speedometer->boxBorderColorsHint( Speedometer::NeedleHead );
QskGradient gradient = speedometer->gradientHint( Speedometer::NeedleHead );
QRectF centerNodeRect( center.x() - radius, center.y() - radius, QRectF centerNodeRect( center.x() - radius, center.y() - radius,
2 * radius, 2 * radius ); 2 * radius, 2 * radius );
QskBoxShapeMetrics shapeMetrics( radius, radius, radius, radius );
QskBoxBorderMetrics borderMetrics( 2 );
QskBoxBorderColors borderColors( Qt::red );
QskGradient gradient( Qt::red );
boxNode->setBoxData( centerNodeRect, shapeMetrics, borderMetrics, borderColors, gradient ); boxNode->setBoxData( centerNodeRect, shapeMetrics, borderMetrics, borderColors, gradient );
TicksNode* needleNode; TicksNode* needleNode;
if ( boxNode->childCount() == 0 ) if ( boxNode->childCount() == 0 )
{ {
needleNode = new TicksNode( Qt::red ); needleNode = new TicksNode();
} }
else else
{ {
needleNode = static_cast< TicksNode* >( boxNode->childAtIndex( 0 ) ); needleNode = static_cast< TicksNode* >( boxNode->childAtIndex( 0 ) );
} }
QColor color = speedometer->color( Speedometer::Needle );
needleNode->setColor( color );
auto panelRadius = static_cast< float >( panelRect.width() / 2 ); auto panelRadius = static_cast< float >( panelRect.width() / 2 );
auto needleWidth = 2; // ### do differently somehow auto needleWidth = speedometer->metric( Speedometer::Needle | QskAspect::MinimumWidth );
auto needleMargin = speedometer->metric( Speedometer::Needle | QskAspect::Margin );
QRectF needleRect( center.x() - needleWidth , center.y() - needleWidth , QRectF needleRect( center.x() - needleWidth , center.y() - needleWidth ,
panelRadius - ( needleWidth + 10 ), 2 * needleWidth ); panelRadius - ( needleWidth + needleMargin ), 2 * needleWidth );
float xStart = center.x() - needleWidth ; float xStart = center.x();
float yStart = center.y(); float yStart = center.y();
float angle = speedometer->value(); float angle = speedometer->value();
qreal cosine = qCos( qDegreesToRadians( angle ) ); qreal cosine = qCos( qDegreesToRadians( angle ) );
qreal sine = qSin( qDegreesToRadians( angle ) ); qreal sine = qSin( qDegreesToRadians( angle ) );
float needleRadius = panelRadius - 10; // 10 == margins ### skinhint float needleRadius = panelRadius - needleMargin;
float xEnd = center.x() + needleRadius * cosine; float xEnd = center.x() + needleRadius * cosine;
float yEnd = center.y() + needleRadius * sine; float yEnd = center.y() + needleRadius * sine;