2023-04-06 07:23:37 +00:00
|
|
|
/******************************************************************************
|
2024-01-17 13:31:45 +00:00
|
|
|
* QSkinny - Copyright (C) The authors
|
2023-04-06 07:23:37 +00:00
|
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
2021-10-20 05:50:25 +00:00
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include "QskArcNode.h"
|
2023-04-12 10:19:26 +00:00
|
|
|
#include "QskArcMetrics.h"
|
2024-01-06 14:05:30 +00:00
|
|
|
#include "QskArcShadowNode.h"
|
2023-04-14 10:44:49 +00:00
|
|
|
#include "QskMargins.h"
|
2022-06-01 14:57:57 +00:00
|
|
|
#include "QskGradient.h"
|
2023-04-14 10:44:49 +00:00
|
|
|
#include "QskShapeNode.h"
|
|
|
|
#include "QskStrokeNode.h"
|
|
|
|
#include "QskSGNode.h"
|
2024-01-06 14:05:30 +00:00
|
|
|
#include "QskShadowMetrics.h"
|
2023-04-12 10:19:26 +00:00
|
|
|
|
2023-04-14 10:44:49 +00:00
|
|
|
#include <qpen.h>
|
2023-04-12 10:19:26 +00:00
|
|
|
#include <qpainterpath.h>
|
2022-06-01 14:57:57 +00:00
|
|
|
|
2024-01-06 14:05:30 +00:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
enum NodeRole
|
|
|
|
{
|
|
|
|
ShadowRole,
|
|
|
|
FillRole,
|
|
|
|
BorderRole
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-04-14 10:44:49 +00:00
|
|
|
static inline QskGradient qskEffectiveGradient(
|
2023-04-17 15:29:47 +00:00
|
|
|
const QskGradient& gradient, const QskArcMetrics& metrics )
|
2023-04-14 07:47:10 +00:00
|
|
|
{
|
|
|
|
if ( !gradient.isMonochrome() )
|
2023-04-12 10:19:26 +00:00
|
|
|
{
|
2023-04-14 07:47:10 +00:00
|
|
|
if ( gradient.type() == QskGradient::Stops )
|
|
|
|
{
|
|
|
|
QskGradient g( gradient.stops() );
|
2023-04-17 15:29:47 +00:00
|
|
|
g.setConicDirection( 0.5, 0.5, metrics.startAngle(), 360.0 );
|
2023-04-14 07:47:10 +00:00
|
|
|
|
|
|
|
return g;
|
|
|
|
}
|
2023-04-12 10:19:26 +00:00
|
|
|
}
|
|
|
|
|
2023-04-14 07:47:10 +00:00
|
|
|
return gradient;
|
2022-06-01 14:57:57 +00:00
|
|
|
}
|
2021-10-20 05:50:25 +00:00
|
|
|
|
2023-04-14 10:44:49 +00:00
|
|
|
static inline QRectF qskEffectiveRect(
|
|
|
|
const QRectF& rect, const qreal borderWidth )
|
|
|
|
{
|
|
|
|
if ( borderWidth <= 0.0 )
|
|
|
|
return rect;
|
|
|
|
|
|
|
|
return qskValidOrEmptyInnerRect( rect, QskMargins( 0.5 * borderWidth ) );
|
|
|
|
}
|
|
|
|
|
2024-01-06 14:05:30 +00:00
|
|
|
static void qskUpdateChildren( QSGNode* parentNode, quint8 role, QSGNode* node )
|
|
|
|
{
|
|
|
|
static const QVector< quint8 > roles = { ShadowRole, FillRole, BorderRole };
|
|
|
|
|
|
|
|
auto oldNode = QskSGNode::findChildNode( parentNode, role );
|
|
|
|
QskSGNode::replaceChildNode( roles, role, parentNode, oldNode, node );
|
|
|
|
}
|
|
|
|
|
2021-10-20 05:50:25 +00:00
|
|
|
QskArcNode::QskArcNode()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QskArcNode::~QskArcNode()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-04-12 10:19:26 +00:00
|
|
|
void QskArcNode::setArcData( const QRectF& rect,
|
2023-04-14 10:44:49 +00:00
|
|
|
const QskArcMetrics& arcMetrics, const QskGradient& fillGradient )
|
2021-10-20 05:50:25 +00:00
|
|
|
{
|
2024-01-06 14:05:30 +00:00
|
|
|
setArcData( rect, arcMetrics, 0.0, QColor(), fillGradient, {}, {} );
|
2023-04-14 10:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics,
|
2024-01-06 14:05:30 +00:00
|
|
|
const qreal borderWidth, const QColor& borderColor, const QskGradient& fillGradient )
|
2023-04-14 10:44:49 +00:00
|
|
|
{
|
2024-01-06 14:05:30 +00:00
|
|
|
setArcData( rect, arcMetrics, borderWidth, borderColor, fillGradient, {}, {} );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics,
|
|
|
|
const qreal borderWidth, const QColor& borderColor, const QskGradient& fillGradient,
|
|
|
|
const QColor& shadowColor, const QskShadowMetrics& shadowMetrics )
|
|
|
|
{
|
2024-06-12 10:40:11 +00:00
|
|
|
const auto metricsArc = arcMetrics.toAbsolute( rect.size() );
|
2024-01-06 14:05:30 +00:00
|
|
|
const auto gradient = qskEffectiveGradient( fillGradient, metricsArc );
|
2023-04-14 10:44:49 +00:00
|
|
|
|
2024-01-06 14:05:30 +00:00
|
|
|
auto shadowNode = static_cast< QskArcShadowNode* >(
|
|
|
|
QskSGNode::findChildNode( this, ShadowRole ) );
|
2023-04-14 10:44:49 +00:00
|
|
|
|
|
|
|
auto fillNode = static_cast< QskShapeNode* >(
|
|
|
|
QskSGNode::findChildNode( this, FillRole ) );
|
|
|
|
|
|
|
|
auto borderNode = static_cast< QskStrokeNode* >(
|
|
|
|
QskSGNode::findChildNode( this, BorderRole ) );
|
|
|
|
|
|
|
|
const auto arcRect = qskEffectiveRect( rect, borderWidth );
|
2024-06-12 10:40:11 +00:00
|
|
|
if ( metricsArc.isNull() || arcRect.isEmpty() )
|
2023-04-14 10:44:49 +00:00
|
|
|
{
|
2024-01-06 14:05:30 +00:00
|
|
|
delete shadowNode;
|
2023-04-14 10:44:49 +00:00
|
|
|
delete fillNode;
|
|
|
|
delete borderNode;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-06-12 10:40:11 +00:00
|
|
|
const auto isFillNodeVisible = gradient.isVisible();
|
2024-05-21 14:08:40 +00:00
|
|
|
const auto isStrokeNodeVisible = ( borderWidth > 0.0 ) && ( borderColor.alpha() > 0 );
|
|
|
|
const auto isShadowNodeVisible = isFillNodeVisible &&
|
|
|
|
shadowColor.isValid() && ( shadowColor.alpha() > 0.0 );
|
2023-04-14 10:44:49 +00:00
|
|
|
|
2024-01-06 14:05:30 +00:00
|
|
|
const auto path = metricsArc.painterPath( arcRect );
|
|
|
|
|
|
|
|
if ( isShadowNodeVisible )
|
|
|
|
{
|
|
|
|
if ( shadowNode == nullptr )
|
|
|
|
{
|
|
|
|
shadowNode = new QskArcShadowNode;
|
|
|
|
QskSGNode::setNodeRole( shadowNode, ShadowRole );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
The shader of the shadow node is for circular arcs and we have some
|
|
|
|
unwanted scaling issues for the spread/blur values when having ellipsoid
|
|
|
|
arcs. We might also want to add the spread value to the ends of the arc
|
|
|
|
and not only to its radius. TODO ...
|
|
|
|
*/
|
|
|
|
|
|
|
|
const auto sm = shadowMetrics.toAbsolute( arcRect.size() );
|
|
|
|
const auto shadowRect = sm.shadowRect( arcRect );
|
|
|
|
const auto spreadRadius = sm.spreadRadius() + 0.5 * metricsArc.thickness();
|
|
|
|
|
|
|
|
shadowNode->setShadowData( shadowRect, spreadRadius, sm.blurRadius(),
|
|
|
|
metricsArc.startAngle(), metricsArc.spanAngle(), shadowColor );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
delete shadowNode;
|
|
|
|
shadowNode = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( isFillNodeVisible )
|
2023-04-14 10:44:49 +00:00
|
|
|
{
|
|
|
|
if ( fillNode == nullptr )
|
|
|
|
{
|
|
|
|
fillNode = new QskShapeNode;
|
|
|
|
QskSGNode::setNodeRole( fillNode, FillRole );
|
|
|
|
}
|
|
|
|
|
|
|
|
fillNode->updateNode( path, QTransform(), arcRect, gradient );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
delete fillNode;
|
2024-01-06 14:05:30 +00:00
|
|
|
fillNode = nullptr;
|
2023-04-14 10:44:49 +00:00
|
|
|
}
|
|
|
|
|
2024-01-06 14:05:30 +00:00
|
|
|
if ( isStrokeNodeVisible )
|
2023-04-14 10:44:49 +00:00
|
|
|
{
|
|
|
|
if ( borderNode == nullptr )
|
|
|
|
{
|
|
|
|
borderNode = new QskStrokeNode;
|
|
|
|
QskSGNode::setNodeRole( borderNode, BorderRole );
|
|
|
|
}
|
|
|
|
|
|
|
|
QPen pen( borderColor, borderWidth );
|
|
|
|
pen.setCapStyle( Qt::FlatCap );
|
2024-01-06 14:05:30 +00:00
|
|
|
|
2023-04-14 10:44:49 +00:00
|
|
|
borderNode->updateNode( path, QTransform(), pen );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
delete borderNode;
|
2024-01-06 14:05:30 +00:00
|
|
|
borderNode = nullptr;
|
2023-04-14 10:44:49 +00:00
|
|
|
}
|
2024-01-06 14:05:30 +00:00
|
|
|
|
2024-05-21 14:08:40 +00:00
|
|
|
qskUpdateChildren( this, ShadowRole, shadowNode );
|
|
|
|
qskUpdateChildren( this, FillRole, fillNode );
|
|
|
|
qskUpdateChildren( this, BorderRole, borderNode );
|
2021-10-20 05:50:25 +00:00
|
|
|
}
|