2019-06-20 12:02:28 +02:00
|
|
|
/******************************************************************************
|
2024-01-17 14:31:45 +01:00
|
|
|
* QSkinny - Copyright (C) The authors
|
2023-04-06 09:23:37 +02:00
|
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
2019-06-20 12:02:28 +02:00
|
|
|
*****************************************************************************/
|
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
#include "DialSkinlet.h"
|
|
|
|
#include "Dial.h"
|
2018-04-05 11:23:38 +02:00
|
|
|
|
|
|
|
#include <QskBoxBorderColors.h>
|
|
|
|
#include <QskBoxBorderMetrics.h>
|
|
|
|
#include <QskBoxShapeMetrics.h>
|
|
|
|
#include <QskTextColors.h>
|
2020-03-13 13:32:22 +01:00
|
|
|
#include <QskTextOptions.h>
|
2023-06-05 10:42:05 +02:00
|
|
|
#include <QskLinesNode.h>
|
2020-03-13 13:32:22 +01:00
|
|
|
#include <QskFunctions.h>
|
2023-06-05 10:42:05 +02:00
|
|
|
#include <QskSGNode.h>
|
2018-04-05 11:23:38 +02:00
|
|
|
|
|
|
|
#include <QFontMetrics>
|
2020-12-18 16:32:54 +01:00
|
|
|
#include <QLineF>
|
2018-04-05 11:23:38 +02:00
|
|
|
#include <QtMath>
|
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
DialSkinlet::DialSkinlet( QskSkin* skin )
|
2018-08-03 08:15:28 +02:00
|
|
|
: QskSkinlet( skin )
|
2018-04-05 11:23:38 +02:00
|
|
|
{
|
2020-12-18 16:32:54 +01:00
|
|
|
setNodeRoles( { PanelRole, NeedleRole, KnobRole, LabelsRole } );
|
2018-04-05 11:23:38 +02:00
|
|
|
}
|
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
QRectF DialSkinlet::subControlRect( const QskSkinnable* skinnable,
|
2020-12-18 16:32:54 +01:00
|
|
|
const QRectF& contentsRect, QskAspect::Subcontrol subcontrol ) const
|
2018-04-05 11:23:38 +02:00
|
|
|
{
|
2020-12-18 16:32:54 +01:00
|
|
|
QRectF r;
|
2020-12-13 19:38:46 +01:00
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
if ( subcontrol == Dial::Knob )
|
2020-12-18 16:32:54 +01:00
|
|
|
{
|
2022-07-14 18:50:17 +02:00
|
|
|
const auto size = skinnable->strutSizeHint( Dial::Knob );
|
2020-12-18 16:32:54 +01:00
|
|
|
r.setSize( size );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const auto extent = qMin( contentsRect.width(), contentsRect.height() );
|
|
|
|
r.setSize( QSizeF( extent, extent ) );
|
|
|
|
}
|
2021-08-04 09:31:16 +02:00
|
|
|
|
2020-12-13 19:38:46 +01:00
|
|
|
r.moveCenter( contentsRect.center() );
|
|
|
|
|
|
|
|
return r;
|
2018-04-05 11:23:38 +02:00
|
|
|
}
|
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
QSGNode* DialSkinlet::updateSubNode(
|
2018-08-03 08:15:28 +02:00
|
|
|
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
|
2018-04-05 11:23:38 +02:00
|
|
|
{
|
2022-07-14 18:50:17 +02:00
|
|
|
const auto speedometer = static_cast< const Dial* >( skinnable );
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
switch ( nodeRole )
|
2018-04-05 11:23:38 +02:00
|
|
|
{
|
|
|
|
case PanelRole:
|
2022-07-14 18:50:17 +02:00
|
|
|
return updateBoxNode( speedometer, node, Dial::Panel );
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2020-12-18 16:32:54 +01:00
|
|
|
case KnobRole:
|
2022-07-14 18:50:17 +02:00
|
|
|
return updateBoxNode( speedometer, node, Dial::Knob );
|
2018-04-05 11:23:38 +02:00
|
|
|
|
|
|
|
case NeedleRole:
|
|
|
|
return updateNeedleNode( speedometer, node );
|
|
|
|
|
2020-12-18 16:32:54 +01:00
|
|
|
case LabelsRole:
|
|
|
|
return updateLabelsNode( speedometer, node );
|
|
|
|
|
|
|
|
|
2018-04-05 11:23:38 +02:00
|
|
|
default:
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
QSGNode* DialSkinlet::updateLabelsNode(
|
|
|
|
const Dial* dial, QSGNode* node ) const
|
2018-04-05 11:23:38 +02:00
|
|
|
{
|
2022-07-14 18:50:17 +02:00
|
|
|
using Q = Dial;
|
2020-12-13 14:23:17 +01:00
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
const auto labels = dial->tickLabels();
|
2018-04-05 11:23:52 +02:00
|
|
|
|
|
|
|
// ### actually, we could draw labels with only one entry
|
2020-12-18 16:32:54 +01:00
|
|
|
if ( labels.count() <= 1 )
|
2018-04-05 11:23:52 +02:00
|
|
|
return nullptr;
|
|
|
|
|
2023-06-05 10:42:05 +02:00
|
|
|
auto ticksNode = QskSGNode::ensureNode< QskLinesNode >( node );
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
const auto color = dial->color( Q::TickLabels );
|
2018-05-09 08:31:43 +02:00
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
const auto startAngle = dial->minimum();
|
|
|
|
const auto endAngle = dial->maximum();
|
2020-12-18 16:32:54 +01:00
|
|
|
const auto step = ( endAngle - startAngle ) / ( labels.count() - 1 );
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
auto scaleRect = this->scaleRect( dial );
|
2019-01-07 09:13:53 +01:00
|
|
|
|
2020-12-18 16:32:54 +01:00
|
|
|
const auto center = scaleRect.center();
|
|
|
|
const auto radius = 0.5 * scaleRect.width();
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
const auto spacing = dial->spacingHint( Q::TickLabels );
|
2022-05-30 15:16:46 +02:00
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
const auto font = dial->effectiveFont( Q::TickLabels );
|
2022-05-30 15:16:46 +02:00
|
|
|
const QFontMetricsF fontMetrics( font );
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2018-05-09 08:31:43 +02:00
|
|
|
auto angle = startAngle;
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
const auto tickSize = dial->strutSizeHint( Q::TickLabels );
|
2020-12-16 12:49:24 +01:00
|
|
|
const auto needleRadius = radius - tickSize.height();
|
|
|
|
|
2018-04-05 11:23:38 +02:00
|
|
|
// Create a series of tickmarks from minimum to maximum
|
2022-05-30 15:16:46 +02:00
|
|
|
|
|
|
|
auto labelNode = ticksNode->firstChild();
|
2023-06-05 10:42:05 +02:00
|
|
|
QVector< QLineF > ticks;
|
2022-05-30 15:16:46 +02:00
|
|
|
|
2020-12-18 16:32:54 +01:00
|
|
|
for ( int i = 0; i < labels.count(); ++i, angle += step )
|
2018-04-05 11:23:38 +02:00
|
|
|
{
|
2020-12-16 12:49:24 +01:00
|
|
|
const qreal cos = qFastCos( qDegreesToRadians( angle ) );
|
|
|
|
const qreal sin = qFastSin( qDegreesToRadians( angle ) );
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2020-12-16 12:49:24 +01:00
|
|
|
const auto xStart = center.x() + radius * cos;
|
|
|
|
const auto yStart = center.y() + radius * sin;
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2020-12-16 12:49:24 +01:00
|
|
|
const auto xEnd = center.x() + needleRadius * cos;
|
|
|
|
const auto yEnd = center.y() + needleRadius * sin;
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2023-06-05 10:42:05 +02:00
|
|
|
ticks += QLineF( xStart, yStart, xEnd, yEnd );
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2022-05-30 15:16:46 +02:00
|
|
|
const auto& text = labels.at( i );
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2022-05-30 15:16:46 +02:00
|
|
|
if ( !text.isEmpty() )
|
|
|
|
{
|
2020-12-16 12:49:24 +01:00
|
|
|
const auto w = qskHorizontalAdvance( fontMetrics, text );
|
|
|
|
const auto h = fontMetrics.height();
|
|
|
|
const auto adjustX = ( -0.5 * cos - 0.5 ) * w;
|
|
|
|
const auto adjustY = ( -0.5 * sin - 0.5 ) * h;
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2020-12-16 12:49:24 +01:00
|
|
|
const auto numbersX = xEnd - ( spacing * cos ) + adjustX;
|
|
|
|
const auto numbersY = yEnd - ( spacing * sin ) + adjustY;
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2020-12-18 16:32:54 +01:00
|
|
|
const QRectF numbersRect( numbersX, numbersY, w, h );
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2022-05-30 15:16:46 +02:00
|
|
|
labelNode = QskSkinlet::updateTextNode(
|
2022-07-14 18:50:17 +02:00
|
|
|
dial, labelNode, numbersRect, Qt::AlignCenter | Qt::AlignHCenter,
|
2022-05-30 15:16:46 +02:00
|
|
|
text, font, QskTextOptions(), QskTextColors( color ), Qsk::Normal );
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2022-05-30 15:16:46 +02:00
|
|
|
if ( labelNode )
|
2018-04-05 11:23:52 +02:00
|
|
|
{
|
2022-05-30 15:16:46 +02:00
|
|
|
if ( labelNode->parent() != ticksNode )
|
|
|
|
ticksNode->appendChildNode( labelNode );
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2022-05-30 15:16:46 +02:00
|
|
|
labelNode = labelNode->nextSibling();
|
|
|
|
}
|
2018-04-05 11:23:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-05 10:42:05 +02:00
|
|
|
ticksNode->updateLines( color, tickSize.width(), ticks );
|
2018-04-05 11:23:38 +02:00
|
|
|
|
|
|
|
return ticksNode;
|
|
|
|
}
|
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
QSGNode* DialSkinlet::updateNeedleNode(
|
|
|
|
const Dial* dial, QSGNode* node ) const
|
2018-04-05 11:23:38 +02:00
|
|
|
{
|
2022-07-14 18:50:17 +02:00
|
|
|
using Q = Dial;
|
2020-12-13 14:23:17 +01:00
|
|
|
|
2023-06-05 10:42:05 +02:00
|
|
|
const auto color = dial->color( Q::Needle );
|
|
|
|
const auto width = dial->metric( Q::Needle | QskAspect::Size );
|
2018-05-09 08:47:52 +02:00
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
const auto line = needlePoints( dial );
|
2019-04-25 14:23:39 +02:00
|
|
|
|
2023-06-05 10:42:05 +02:00
|
|
|
auto needleNode = QskSGNode::ensureNode< QskLinesNode >( node );
|
|
|
|
needleNode->updateLine( color, width * 2, line.p1(), line.p2() );
|
2019-04-25 14:23:39 +02:00
|
|
|
|
2020-12-18 16:32:54 +01:00
|
|
|
return needleNode;
|
|
|
|
}
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
QRectF DialSkinlet::scaleRect( const Dial* dial ) const
|
2020-12-18 16:32:54 +01:00
|
|
|
{
|
2022-07-14 18:50:17 +02:00
|
|
|
using Q = Dial;
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
const auto margins = dial->marginHint( Q::Panel );
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
auto r = dial->subControlRect( Q::Panel );
|
2020-12-18 16:32:54 +01:00
|
|
|
r = r.marginsRemoved( margins );
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2020-12-18 16:32:54 +01:00
|
|
|
return r;
|
|
|
|
}
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
QLineF DialSkinlet::needlePoints( const Dial* dial ) const
|
2020-12-18 16:32:54 +01:00
|
|
|
{
|
2022-07-14 18:50:17 +02:00
|
|
|
const auto r = scaleRect( dial );
|
|
|
|
const auto margin = dial->metric( Dial::Needle | QskAspect::Margin );
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2020-12-18 16:32:54 +01:00
|
|
|
QLineF line;
|
|
|
|
line.setP1( r.center() );
|
|
|
|
line.setLength( 0.5 * r.width() - margin );
|
2022-07-14 18:50:17 +02:00
|
|
|
line.setAngle( -dial->value() );
|
2018-04-05 11:23:38 +02:00
|
|
|
|
2020-12-18 16:32:54 +01:00
|
|
|
return line;
|
2018-04-05 11:23:38 +02:00
|
|
|
}
|
|
|
|
|
2022-07-14 18:50:17 +02:00
|
|
|
#include "moc_DialSkinlet.cpp"
|