Merge branch 'speedometer' of git://github.com/peter-ha/qskinny into peter-ha-speedometer
This commit is contained in:
commit
b48dec991e
@ -58,5 +58,5 @@ void ButtonBar::addIndicator( const char* name )
|
||||
|
||||
QSizeF ButtonBar::contentsSizeHint() const
|
||||
{
|
||||
return QSizeF( -1, 20 );
|
||||
return { -1, 20 };
|
||||
}
|
||||
|
@ -4,6 +4,9 @@
|
||||
#include "SoundControl.h"
|
||||
#include "ButtonBar.h"
|
||||
|
||||
#include "Speedometer.h"
|
||||
#include "SpeedometerSkinlet.h"
|
||||
|
||||
#include <QskBox.h>
|
||||
#include <QskFunctions.h>
|
||||
#include <QskPushButton.h>
|
||||
@ -80,6 +83,7 @@ DefaultSkin::DefaultSkin( const QString& name, QObject* parent ):
|
||||
m_scheme( Daylight )
|
||||
{
|
||||
setObjectName( "DefaultSkin" );
|
||||
declareSkinlet< Speedometer, SpeedometerSkinlet >();
|
||||
initHints();
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
#include "MainWindow.h"
|
||||
#include "ButtonBar.h"
|
||||
#include "SoundControl.h"
|
||||
#include "SkinFactory.h"
|
||||
#include "SoundControl.h"
|
||||
#include "SpeedometerDisplay.h"
|
||||
|
||||
#include <QskGraphic.h>
|
||||
#include <QskGraphicIO.h>
|
||||
@ -12,35 +13,36 @@
|
||||
#include <QDate>
|
||||
#include <QImage>
|
||||
|
||||
MainWindow::MainWindow()
|
||||
MainWindow::MainWindow() :
|
||||
m_layout( nullptr )
|
||||
{
|
||||
const QImage image( ":/images/background.jpg" );
|
||||
const QImage image( QStringLiteral( ":/images/background.jpg" ) );
|
||||
|
||||
auto backgroundImage = new QskGraphicLabel( contentItem() );
|
||||
backgroundImage->setGraphic( QskGraphic::fromImage( image ) );
|
||||
backgroundImage->setFillMode( QskGraphicLabel::Stretch );
|
||||
|
||||
m_layout = new QskLinearBox( Qt::Vertical, contentItem() );
|
||||
|
||||
auto header = headerBar();
|
||||
auto content = mainContent();
|
||||
auto footer = footerBar();
|
||||
|
||||
auto layout = new QskLinearBox( Qt::Vertical, contentItem() );
|
||||
m_layout->addItem( header );
|
||||
m_layout->setStretchFactor( header, 1 );
|
||||
|
||||
layout->addItem( header );
|
||||
layout->setStretchFactor( header, 1 );
|
||||
m_layout->addItem( content );
|
||||
m_layout->setStretchFactor( content, 10 );
|
||||
|
||||
layout->addItem( content );
|
||||
layout->setStretchFactor( content, 10 );
|
||||
|
||||
layout->addItem( footer );
|
||||
layout->setStretchFactor( footer, 1 );
|
||||
m_layout->addItem( footer );
|
||||
m_layout->setStretchFactor( footer, 1 );
|
||||
|
||||
setAutoLayoutChildren( true );
|
||||
}
|
||||
|
||||
QQuickItem* MainWindow::headerBar() const
|
||||
{
|
||||
auto* header = new ButtonBar();
|
||||
auto* header = new ButtonBar( m_layout );
|
||||
header->addIndicator( "bluetooth" );
|
||||
header->addIndicator( "location" );
|
||||
header->addIndicator( "phone" );
|
||||
@ -56,12 +58,13 @@ QQuickItem* MainWindow::headerBar() const
|
||||
|
||||
QQuickItem* MainWindow::mainContent() const
|
||||
{
|
||||
return new SoundControl();
|
||||
return new SpeedometerDisplay( m_layout );
|
||||
//return new SoundControl(); ###
|
||||
}
|
||||
|
||||
QQuickItem* MainWindow::footerBar() const
|
||||
{
|
||||
auto* footer = new ButtonBar();
|
||||
auto* footer = new ButtonBar( m_layout );
|
||||
|
||||
footer->addIndicator( "cloud" );
|
||||
footer->addIndicator( "man" );
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#include <QskWindow.h>
|
||||
|
||||
class QskLinearBox;
|
||||
|
||||
class QQuickItem;
|
||||
|
||||
class MainWindow : public QskWindow
|
||||
@ -14,6 +16,8 @@ private:
|
||||
QQuickItem* headerBar() const;
|
||||
QQuickItem* mainContent() const;
|
||||
QQuickItem* footerBar() const;
|
||||
|
||||
QskLinearBox* m_layout;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -4,6 +4,9 @@
|
||||
#include "SoundControl.h"
|
||||
#include "ButtonBar.h"
|
||||
|
||||
#include "Speedometer.h"
|
||||
#include "SpeedometerSkinlet.h"
|
||||
|
||||
#include <QskBox.h>
|
||||
#include <QskFunctions.h>
|
||||
#include <QskPushButton.h>
|
||||
@ -57,6 +60,7 @@ OtherSkin::OtherSkin( const QString& name, QObject* parent ):
|
||||
m_palette( new Palette )
|
||||
{
|
||||
setObjectName( "OtherSkin" );
|
||||
declareSkinlet< Speedometer, SpeedometerSkinlet >();
|
||||
initHints();
|
||||
initGraphicFilters();
|
||||
}
|
||||
|
59
examples/automotive/Speedometer.cpp
Normal file
59
examples/automotive/Speedometer.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
#include "Speedometer.h"
|
||||
|
||||
#include <QskSkinlet.h>
|
||||
#include <QskSkinnable.h>
|
||||
|
||||
QSK_SUBCONTROL( Speedometer, Panel )
|
||||
QSK_SUBCONTROL( Speedometer, Labels )
|
||||
QSK_SUBCONTROL( Speedometer, Needle )
|
||||
|
||||
Speedometer::Speedometer( QQuickItem* parent ) :
|
||||
QskControl( 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
|
||||
{
|
||||
return m_labels;
|
||||
}
|
||||
|
||||
void Speedometer::setLabels( const QVector< QString >& labels )
|
||||
{
|
||||
m_labels = labels;
|
||||
}
|
||||
|
||||
#include "moc_Speedometer.cpp"
|
35
examples/automotive/Speedometer.h
Normal file
35
examples/automotive/Speedometer.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef SPEEDOMETER_H
|
||||
#define SPEEDOMETER_H
|
||||
|
||||
#include <QskControl.h>
|
||||
|
||||
class Speedometer : public QskControl
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QSK_SUBCONTROLS( Panel, Labels, 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;
|
||||
};
|
||||
|
||||
#endif // SPEEDOMETER_H
|
118
examples/automotive/SpeedometerDisplay.cpp
Normal file
118
examples/automotive/SpeedometerDisplay.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
#include "SpeedometerDisplay.h"
|
||||
|
||||
#include "Speedometer.h"
|
||||
|
||||
#include <QskEvent.h>
|
||||
#include <QskLinearBox.h>
|
||||
#include <QskTextLabel.h>
|
||||
|
||||
#include <QTime>
|
||||
#include <QTimer>
|
||||
#include <QtGlobal>
|
||||
|
||||
SpeedometerDisplay::SpeedometerDisplay( QQuickItem *parent ) :
|
||||
QskControl( parent ),
|
||||
m_box( new QskLinearBox( Qt::Horizontal, this ) ),
|
||||
m_revCounter( new Speedometer( m_box ) ),
|
||||
m_revCounterText( new QskTextLabel( QStringLiteral( "x 1000 min^-1" ), m_revCounter ) ),
|
||||
m_speedometer( new Speedometer( m_box ) ),
|
||||
m_speedometerText( new QskTextLabel( QStringLiteral( "km/h" ), m_speedometer ) ),
|
||||
m_fuelGauge( new Speedometer( m_box ) ),
|
||||
m_fuelGaugeText( new QskTextLabel( QStringLiteral( "fuel" ), m_fuelGauge ) )
|
||||
{
|
||||
qsrand( static_cast< uint >( QTime::currentTime().msec() ) );
|
||||
|
||||
setPolishOnResize( true );
|
||||
|
||||
m_box->setAutoAddChildren( true );
|
||||
m_box->setAutoLayoutChildren( true );
|
||||
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 );
|
||||
QVector< QString > revCounterLabels;
|
||||
|
||||
for ( int i = 0; i < numberLabels; ++i )
|
||||
{
|
||||
revCounterLabels.append( QString::number( i ) );
|
||||
}
|
||||
|
||||
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 );
|
||||
QVector< QString > speedometerLabels;
|
||||
speedometerLabels.reserve( numberLabels );
|
||||
|
||||
for ( int i = 0; i < numberLabels; ++i )
|
||||
{
|
||||
speedometerLabels.append( QString::number( i * 10 ) );
|
||||
}
|
||||
|
||||
m_speedometer->setLabels( speedometerLabels );
|
||||
|
||||
auto timer = new QTimer( this );
|
||||
|
||||
connect( timer, &QTimer::timeout, this, [ this ]()
|
||||
{
|
||||
auto speedometerValue = m_speedometer->value() + qrand() % 3 - 0.95;
|
||||
m_speedometer->setValue( speedometerValue );
|
||||
|
||||
auto fuelGaugeValue = 0.99997 * m_fuelGauge->value();
|
||||
m_fuelGauge->setValue( fuelGaugeValue );
|
||||
});
|
||||
|
||||
timer->setInterval( 16 );
|
||||
timer->start();
|
||||
|
||||
|
||||
m_fuelGauge->setObjectName( QStringLiteral( "Fuel Gauge" ) );
|
||||
m_fuelGauge->setStartAngle( 195 );
|
||||
m_fuelGauge->setEndAngle( 345 );
|
||||
m_fuelGauge->setValue( 330 );
|
||||
|
||||
QVector< QString > fuelGaugeLabels;
|
||||
fuelGaugeLabels.append( { "0", "", "1/2", "", "1/1" } );
|
||||
|
||||
m_fuelGauge->setLabels( fuelGaugeLabels );
|
||||
|
||||
|
||||
m_revCounterText->setMargins( 50 );
|
||||
m_speedometerText->setMargins( 50 );
|
||||
m_fuelGaugeText->setMargins( 50 );
|
||||
}
|
||||
|
||||
void SpeedometerDisplay::updateLayout()
|
||||
{
|
||||
auto radius = qMin( 0.33 * size().width(), size().height() );
|
||||
auto x = ( width() - radius * 2.7 - 2 * m_box->spacing() ) / 2;
|
||||
auto y = ( height() - radius ) / 2;
|
||||
m_box->setPosition( { x, y } );
|
||||
|
||||
m_revCounter->setFixedSize( radius, radius );
|
||||
QSizeF hint = m_revCounterText->sizeHint();
|
||||
x = ( radius - hint.width() ) / 2;
|
||||
y = ( ( radius - hint.height() ) / 2 ) + m_revCounterText->margins().top();
|
||||
m_revCounterText->setGeometry( x, y, hint.width(), hint.height() );
|
||||
|
||||
m_speedometer->setFixedSize( radius, radius );
|
||||
hint = m_speedometerText->sizeHint();
|
||||
x = ( radius - hint.width() ) / 2;
|
||||
y = ( ( radius - hint.height() ) / 2 ) + m_speedometerText->margins().top();
|
||||
m_speedometerText->setGeometry( x, y, hint.width(), hint.height() );
|
||||
|
||||
m_fuelGauge->setFixedSize( 0.7 * radius, 0.7 * radius );
|
||||
hint = m_fuelGaugeText->sizeHint();
|
||||
x = ( 0.7 * radius - hint.width() ) / 2;
|
||||
y = ( ( 0.7 * radius - hint.height() ) / 2 ) + m_fuelGaugeText->margins().top();
|
||||
m_fuelGaugeText->setGeometry( x, y, hint.width(), hint.height() );
|
||||
}
|
28
examples/automotive/SpeedometerDisplay.h
Normal file
28
examples/automotive/SpeedometerDisplay.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef SPEEDOMETERDISPLAY_H
|
||||
#define SPEEDOMETERDISPLAY_H
|
||||
|
||||
#include <QskControl.h>
|
||||
|
||||
class QskLinearBox;
|
||||
class QskTextLabel;
|
||||
class Speedometer;
|
||||
|
||||
class SpeedometerDisplay : public QskControl
|
||||
{
|
||||
public:
|
||||
SpeedometerDisplay( QQuickItem* parent = nullptr );
|
||||
|
||||
protected:
|
||||
void updateLayout() override;
|
||||
|
||||
private:
|
||||
QskLinearBox* m_box;
|
||||
Speedometer* m_revCounter;
|
||||
QskTextLabel* m_revCounterText;
|
||||
Speedometer* m_speedometer;
|
||||
QskTextLabel* m_speedometerText;
|
||||
Speedometer* m_fuelGauge;
|
||||
QskTextLabel* m_fuelGaugeText;
|
||||
};
|
||||
|
||||
#endif // SPEEDOMETERDISPLAY_H
|
277
examples/automotive/SpeedometerSkinlet.cpp
Normal file
277
examples/automotive/SpeedometerSkinlet.cpp
Normal file
@ -0,0 +1,277 @@
|
||||
#include "SpeedometerSkinlet.h"
|
||||
#include "Speedometer.h"
|
||||
|
||||
#include <QskBoxBorderColors.h>
|
||||
#include <QskBoxBorderMetrics.h>
|
||||
#include <QskBoxNode.h>
|
||||
#include <QskBoxShapeMetrics.h>
|
||||
#include <QskTextColors.h>
|
||||
#include <QskTextNode.h>
|
||||
#include <QskTextOptions.h> // ### remove
|
||||
|
||||
#include <QFontMetrics>
|
||||
#include <QSGFlatColorMaterial>
|
||||
#include <QtMath>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class TicksNode : public QSGGeometryNode
|
||||
{
|
||||
public:
|
||||
TicksNode( const QColor& color ):
|
||||
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 );
|
||||
}
|
||||
|
||||
private:
|
||||
QSGFlatColorMaterial m_material;
|
||||
QSGGeometry m_geometry;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
SpeedometerSkinlet::SpeedometerSkinlet( QskSkin* skin ) :
|
||||
QskSkinlet( skin )
|
||||
{
|
||||
setNodeRoles( { PanelRole, LabelsRole, NeedleRole } );
|
||||
}
|
||||
|
||||
SpeedometerSkinlet::~SpeedometerSkinlet() = default;
|
||||
|
||||
QRectF SpeedometerSkinlet::subControlRect( const QskSkinnable* skinnable,
|
||||
QskAspect::Subcontrol /*unused*/ ) const
|
||||
{
|
||||
const auto speedometer = static_cast< const Speedometer* >( skinnable );
|
||||
|
||||
// ### differentiate for subcontrols
|
||||
return speedometer->contentsRect();
|
||||
}
|
||||
|
||||
QSGNode* SpeedometerSkinlet::updateSubNode( const QskSkinnable* skinnable, quint8 nodeRole,
|
||||
QSGNode* node ) const
|
||||
{
|
||||
const auto speedometer = static_cast< const Speedometer* >( skinnable );
|
||||
|
||||
switch( nodeRole )
|
||||
{
|
||||
case PanelRole:
|
||||
return updatePanelNode( speedometer, node );
|
||||
|
||||
case LabelsRole:
|
||||
return updateLabelsNode( speedometer, node );
|
||||
|
||||
case NeedleRole:
|
||||
return updateNeedleNode( 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;
|
||||
}
|
||||
|
||||
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 );
|
||||
boxNode->setBoxData( panelRect, shapeMetrics, borderMetrics, borderColors, gradient );
|
||||
|
||||
return boxNode;
|
||||
}
|
||||
|
||||
QSGNode* SpeedometerSkinlet::updateLabelsNode( const Speedometer* speedometer, QSGNode* node ) const
|
||||
{
|
||||
const int labelsCount = speedometer->labels().count();
|
||||
|
||||
// ### actually, we could draw labels with only one entry
|
||||
if ( labelsCount <= 1 )
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto ticksNode = static_cast< TicksNode* >( node );
|
||||
|
||||
if ( ticksNode == nullptr )
|
||||
{
|
||||
ticksNode = new TicksNode( Qt::white );
|
||||
}
|
||||
|
||||
const float startAngle = speedometer->startAngle();
|
||||
const float endAngle = speedometer->endAngle();
|
||||
const auto step = ( endAngle - startAngle ) / ( labelsCount - 1 );
|
||||
|
||||
auto geometry = ticksNode->geometry();
|
||||
geometry->allocate( labelsCount * 2 );
|
||||
|
||||
auto vertexData = geometry->vertexDataAsPoint2D();
|
||||
memset( vertexData, 0, static_cast< size_t >( geometry->vertexCount() ) );
|
||||
|
||||
QMarginsF panelMargins = speedometer->marginsHint( Speedometer::Panel | QskAspect::Margin );
|
||||
const QRectF panelRect = subControlRect( speedometer,
|
||||
Speedometer::Panel ).marginsRemoved( panelMargins );
|
||||
|
||||
QPointF center = QPointF( panelRect.x() + panelRect.width() / 2,
|
||||
panelRect.y() + panelRect.height() / 2 );
|
||||
auto radius = static_cast< float >( panelRect.width() / 2 );
|
||||
|
||||
const QMarginsF numbersMargins = speedometer->marginsHint( Speedometer::Labels | QskAspect::Margin );
|
||||
QFontMetrics fontMetrics( speedometer->effectiveFont( Speedometer::Labels ) );
|
||||
|
||||
float angle = startAngle;
|
||||
|
||||
// Create a series of tickmarks from minimum to maximum
|
||||
for( int i = 0; i < labelsCount; ++i, angle += step )
|
||||
{
|
||||
qreal cosine = qCos( qDegreesToRadians( angle ) );
|
||||
qreal sine = qSin( qDegreesToRadians( angle ) );
|
||||
|
||||
float xStart = center.x() + radius * cosine;
|
||||
float 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;
|
||||
|
||||
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;
|
||||
|
||||
float numbersX = xEnd + ( -1 * numbersMargins.left() * cosine ) + adjustX;
|
||||
float numbersY = yEnd + ( -1 * numbersMargins.top() * sine ) + adjustY;
|
||||
|
||||
QRectF numbersRect( numbersX, numbersY, w, h );
|
||||
|
||||
QskTextNode* numbersNode;
|
||||
|
||||
if ( ticksNode->childCount() > i )
|
||||
{
|
||||
numbersNode = static_cast< QskTextNode* >( ticksNode->childAtIndex( i ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
numbersNode = new QskTextNode();
|
||||
}
|
||||
|
||||
numbersNode->setTextData( speedometer, text, numbersRect, QFont(),
|
||||
QskTextOptions(), QskTextColors( Qt::white ),
|
||||
Qt::AlignCenter | Qt::AlignHCenter, Qsk::Normal );
|
||||
|
||||
if ( ticksNode->childCount() <= i )
|
||||
{
|
||||
ticksNode->appendChildNode( numbersNode );
|
||||
}
|
||||
// ### remove nodes in case they are superfluous
|
||||
}
|
||||
}
|
||||
|
||||
geometry->setLineWidth( 2 );
|
||||
geometry->markVertexDataDirty();
|
||||
|
||||
ticksNode->markDirty( QSGNode::DirtyGeometry );
|
||||
|
||||
return ticksNode;
|
||||
}
|
||||
|
||||
QSGNode* SpeedometerSkinlet::updateNeedleNode( const Speedometer* speedometer, QSGNode* node ) const
|
||||
{
|
||||
QMarginsF margins = speedometer->marginsHint( Speedometer::Panel | QskAspect::Margin );
|
||||
const QRectF panelRect = subControlRect( speedometer, Speedometer::Panel ).marginsRemoved( margins );
|
||||
auto radius = 15; // ### skin hint
|
||||
QPointF center = QPointF( panelRect.x() + panelRect.width() / 2,
|
||||
panelRect.y() + panelRect.height() / 2 );
|
||||
|
||||
auto boxNode = static_cast< QskBoxNode* >( node );
|
||||
|
||||
if( boxNode == nullptr )
|
||||
{
|
||||
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 );
|
||||
boxNode->setBoxData( centerNodeRect, shapeMetrics, borderMetrics, borderColors, gradient );
|
||||
|
||||
TicksNode* needleNode;
|
||||
|
||||
if ( boxNode->childCount() == 0 )
|
||||
{
|
||||
needleNode = new TicksNode( Qt::red );
|
||||
}
|
||||
else
|
||||
{
|
||||
needleNode = static_cast< TicksNode* >( boxNode->childAtIndex( 0 ) );
|
||||
}
|
||||
|
||||
auto panelRadius = static_cast< float >( panelRect.width() / 2 );
|
||||
|
||||
auto needleWidth = 2; // ### do differently somehow
|
||||
QRectF needleRect( center.x() - needleWidth , center.y() - needleWidth ,
|
||||
panelRadius - ( needleWidth + 10 ), 2 * needleWidth );
|
||||
float xStart = center.x() - needleWidth ;
|
||||
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 xEnd = center.x() + needleRadius * cosine;
|
||||
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 );
|
||||
|
||||
if ( boxNode->childCount() == 0 )
|
||||
{
|
||||
boxNode->appendChildNode( needleNode );
|
||||
}
|
||||
|
||||
return boxNode;
|
||||
}
|
||||
|
||||
#include "moc_SpeedometerSkinlet.cpp"
|
36
examples/automotive/SpeedometerSkinlet.h
Normal file
36
examples/automotive/SpeedometerSkinlet.h
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef SPEEDOMETERSKINLET_H
|
||||
#define SPEEDOMETERSKINLET_H
|
||||
|
||||
#include <QskSkinlet.h>
|
||||
|
||||
class Speedometer;
|
||||
|
||||
class SpeedometerSkinlet : public QskSkinlet
|
||||
{
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
|
||||
enum NodeRole
|
||||
{
|
||||
PanelRole,
|
||||
LabelsRole,
|
||||
NeedleRole
|
||||
};
|
||||
|
||||
Q_INVOKABLE SpeedometerSkinlet( QskSkin* skin = nullptr );
|
||||
virtual ~SpeedometerSkinlet() override;
|
||||
|
||||
virtual QRectF subControlRect( const QskSkinnable* skinnable,
|
||||
QskAspect::Subcontrol ) const override;
|
||||
|
||||
protected:
|
||||
virtual QSGNode* updateSubNode( const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const override;
|
||||
|
||||
private:
|
||||
QSGNode* updatePanelNode( const Speedometer*, QSGNode* ) const;
|
||||
QSGNode* updateLabelsNode( const Speedometer*, QSGNode* ) const;
|
||||
QSGNode* updateNeedleNode( const Speedometer*, QSGNode* ) const;
|
||||
};
|
||||
|
||||
#endif // SPEEDOMETERSKINLET_H
|
@ -18,7 +18,10 @@ HEADERS += \
|
||||
SkinFactory.h \
|
||||
DefaultSkin.h \
|
||||
OtherSkin.h \
|
||||
MainWindow.h
|
||||
MainWindow.h \
|
||||
Speedometer.h \
|
||||
SpeedometerSkinlet.h \
|
||||
SpeedometerDisplay.h
|
||||
|
||||
SOURCES += \
|
||||
ButtonBar.cpp \
|
||||
@ -27,7 +30,10 @@ SOURCES += \
|
||||
DefaultSkin.cpp \
|
||||
OtherSkin.cpp \
|
||||
MainWindow.cpp \
|
||||
main.cpp
|
||||
main.cpp \
|
||||
Speedometer.cpp \
|
||||
SpeedometerSkinlet.cpp \
|
||||
SpeedometerDisplay.cpp
|
||||
|
||||
QRCFILES += \
|
||||
images.qrc
|
||||
|
@ -16,7 +16,8 @@ int main( int argc, char** argv )
|
||||
auto skinFactory = new SkinFactory();
|
||||
|
||||
qskSkinManager->setPluginPaths( QStringList() ); // no plugins
|
||||
qskSkinManager->registerFactory( "SampleSkinFactory", skinFactory );
|
||||
qskSkinManager->registerFactory( QStringLiteral( "SampleSkinFactory" ),
|
||||
skinFactory );
|
||||
|
||||
QGuiApplication app( argc, argv );
|
||||
|
||||
@ -33,7 +34,7 @@ int main( int argc, char** argv )
|
||||
// CTRL-S allow to rotate through the registered skins and CTRL-T
|
||||
// changes the colors, when the DefaultSkin is active.
|
||||
|
||||
qskSetup->setSkin( "DefaultSkin" );
|
||||
qskSetup->setSkin( QStringLiteral( "DefaultSkin" ) );
|
||||
|
||||
cout << "CTRL-S to change the skin." << endl;
|
||||
cout << "CTRL-T to change the color scheme, when the \"Default\" skin is active." << endl;
|
||||
|
@ -23,7 +23,7 @@ public:
|
||||
};
|
||||
|
||||
SliderSkinlet();
|
||||
virtual ~SliderSkinlet();
|
||||
virtual ~SliderSkinlet() override;
|
||||
|
||||
virtual QRectF subControlRect( const QskSkinnable*,
|
||||
QskAspect::Subcontrol ) const override;
|
||||
|
Loading…
x
Reference in New Issue
Block a user