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;
}
}
} // namespace
DefaultSkin::DefaultSkin( const QString& name, QObject* parent ):
QskSkin( parent ),
@ -101,7 +101,8 @@ void DefaultSkin::initHints()
setColor( QskTextLabel::Text, m_palette->color4 );
// - sound control
// -- sound control
setGradient( SoundControl::Overlay, 0 );
setGradient( SoundControl::CrossHair, m_palette->color3 );
setGradient( SoundControl::Marker, m_palette->color5 );
@ -146,6 +147,30 @@ void DefaultSkin::initHints()
setBoxShape( QskSlider::Handle, 100.0, Qt::RelativeSize );
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
for ( auto state : { QskAspect::NoState, QskSlider::Pressed } )
{

View File

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

View File

@ -140,6 +140,29 @@ void OtherSkin::initHints()
setBoxBorderMetrics( QskSlider::Handle, 0 );
setBoxShape( QskSlider::Handle, 6 );
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()

View File

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

View File

@ -5,47 +5,14 @@
QSK_SUBCONTROL( Speedometer, Panel )
QSK_SUBCONTROL( Speedometer, Labels )
QSK_SUBCONTROL( Speedometer, NeedleHead )
QSK_SUBCONTROL( Speedometer, Needle )
Speedometer::Speedometer( QQuickItem* parent ) :
QskControl( parent ),
m_value( 0.0 ),
m_startAngle( -215 ),
m_endAngle( 35 )
QskRangeControl( parent )
{
}
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
{
return m_labels;

View File

@ -1,33 +1,21 @@
#ifndef SPEEDOMETER_H
#define SPEEDOMETER_H
#include <QskControl.h>
#include <QskRangeControl.h>
class Speedometer : public QskControl
class Speedometer : public QskRangeControl
{
Q_OBJECT
public:
QSK_SUBCONTROLS( Panel, Labels, Needle )
QSK_SUBCONTROLS( Panel, Labels, NeedleHead, Needle )
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;
void setLabels( const QVector< QString >& labels );
private:
float m_value;
float m_startAngle;
float m_endAngle;
float m_labelsStep;
QVector< QString > m_labels;
};

View File

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

View File

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