qskinny/src/nodes/QskArcNode.cpp

142 lines
4.3 KiB
C++
Raw Normal View History

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
*****************************************************************************/
#include "QskArcNode.h"
#include "QskArcMetrics.h"
#include "QskArcShadowNode.h"
2024-09-11 08:24:22 +00:00
#include "QskArcRenderNode.h"
#include "QskArcRenderer.h"
#include "QskMargins.h"
2022-06-01 14:57:57 +00:00
#include "QskGradient.h"
#include "QskShapeNode.h"
#include "QskSGNode.h"
#include "QskShadowMetrics.h"
#include <qpainterpath.h>
2022-06-01 14:57:57 +00:00
namespace
{
enum NodeRole
{
ShadowRole,
2024-09-11 08:24:22 +00:00
PathRole,
ArcRole
};
}
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-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-14 07:47:10 +00:00
return gradient;
2022-06-01 14:57:57 +00:00
}
2024-09-11 08:24:22 +00:00
template< typename Node >
inline Node* qskInsertOrRemoveNode( QSGNode* parentNode, quint8 role, bool isValid )
{
2024-09-11 08:24:22 +00:00
using namespace QskSGNode;
2024-09-11 08:24:22 +00:00
Node* oldNode = static_cast< Node* >( findChildNode( parentNode, role ) );
Node* newNode = isValid ? ensureNode< Node >( oldNode ) : nullptr;
2024-09-11 08:24:22 +00:00
static const QVector< quint8 > roles = { ShadowRole, PathRole, ArcRole };
replaceChildNode( roles, role, parentNode, oldNode, newNode );
2024-09-11 08:24:22 +00:00
return newNode;
}
QskArcNode::QskArcNode()
{
}
QskArcNode::~QskArcNode()
{
}
void QskArcNode::setArcData( const QRectF& rect,
2024-09-11 08:24:22 +00:00
const QskArcMetrics& arcMetrics, const QskGradient& gradient )
{
2024-09-11 08:24:22 +00:00
setArcData( rect, arcMetrics, 0.0, QColor(), gradient, {}, {} );
}
void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics,
2024-09-11 08:24:22 +00:00
const qreal borderWidth, const QColor& borderColor, const QskGradient& gradient )
{
2024-09-11 08:24:22 +00:00
setArcData( rect, arcMetrics, borderWidth, borderColor, gradient, {}, {} );
}
void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics,
2024-09-11 08:24:22 +00:00
const qreal borderWidth, const QColor& borderColor, const QskGradient& gradient,
const QColor& shadowColor, const QskShadowMetrics& shadowMetrics )
{
2024-09-11 08:24:22 +00:00
const bool radial = false;
2024-09-11 08:24:22 +00:00
const auto metricsArc = arcMetrics.toAbsolute( rect.size() );
2024-09-11 08:24:22 +00:00
if ( metricsArc.isNull() || rect.isEmpty() )
{
2024-09-11 08:24:22 +00:00
delete QskSGNode::findChildNode( this, ShadowRole );
delete QskSGNode::findChildNode( this, PathRole );
delete QskSGNode::findChildNode( this, ArcRole );
return;
}
2024-09-11 08:24:22 +00:00
const auto hasFilling = gradient.isVisible();
const auto hasBorder = ( borderWidth > 0.0 )
&& borderColor.isValid() && ( borderColor.alpha() > 0 );
const auto hasShadow = shadowColor.isValid() && ( shadowColor.alpha() > 0 );
2024-09-11 08:24:22 +00:00
auto shadowNode = qskInsertOrRemoveNode< QskArcShadowNode >(
this, ShadowRole, hasFilling && hasShadow );
2024-09-11 08:24:22 +00:00
if ( shadowNode )
{
/*
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 ...
*/
2024-09-11 08:24:22 +00:00
const auto sm = shadowMetrics.toAbsolute( rect.size() );
const auto shadowRect = sm.shadowRect( rect );
const auto spreadRadius = sm.spreadRadius() + 0.5 * metricsArc.thickness();
shadowNode->setShadowData( shadowRect, spreadRadius, sm.blurRadius(),
metricsArc.startAngle(), metricsArc.spanAngle(), shadowColor );
}
2024-09-11 08:24:22 +00:00
auto pathNode = qskInsertOrRemoveNode< QskShapeNode >( this, PathRole,
hasFilling && !QskArcRenderer::isGradientSupported( rect, metricsArc, gradient ) );
2024-09-11 08:24:22 +00:00
if ( pathNode )
{
2024-09-11 08:24:22 +00:00
const auto path = metricsArc.painterPath( rect, radial );
pathNode->updateNode( path, QTransform(), rect,
qskEffectiveGradient( gradient, metricsArc ) );
}
2024-09-11 08:24:22 +00:00
auto arcNode = qskInsertOrRemoveNode< QskArcRenderNode >(
this, ArcRole, hasBorder || ( hasFilling && !pathNode ) );
2024-09-11 08:24:22 +00:00
if ( arcNode )
{
2024-09-11 08:24:22 +00:00
arcNode->updateNode( rect, metricsArc, radial,
borderWidth, borderColor, pathNode ? QskGradient() : gradient );
}
}