2022-12-05 11:34:08 +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
|
2022-12-05 11:34:08 +00:00
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include "QskBoxRectangleNode.h"
|
|
|
|
#include "QskBoxBorderColors.h"
|
|
|
|
#include "QskBoxBorderMetrics.h"
|
|
|
|
#include "QskBoxRenderer.h"
|
|
|
|
#include "QskBoxShapeMetrics.h"
|
|
|
|
#include "QskGradient.h"
|
|
|
|
#include "QskGradientDirection.h"
|
2023-11-15 10:47:56 +00:00
|
|
|
#include "QskFillNodePrivate.h"
|
2022-12-05 11:34:08 +00:00
|
|
|
|
2024-09-23 12:56:39 +00:00
|
|
|
static inline bool qskHasBorder(
|
|
|
|
const QskBoxBorderMetrics& metrics, const QskBoxBorderColors& colors )
|
|
|
|
{
|
|
|
|
return !metrics.isNull() && colors.isVisible();
|
|
|
|
}
|
|
|
|
|
2024-09-17 11:57:10 +00:00
|
|
|
class QskBoxRectangleNodePrivate final : public QskFillNodePrivate
|
2022-12-05 11:34:08 +00:00
|
|
|
{
|
2024-09-17 11:57:10 +00:00
|
|
|
public:
|
|
|
|
inline void resetNode( QskBoxRectangleNode* node )
|
|
|
|
{
|
|
|
|
m_metricsHash = m_colorsHash = 0;
|
|
|
|
node->resetGeometry();
|
|
|
|
}
|
2022-12-05 11:34:08 +00:00
|
|
|
|
2024-09-20 09:58:32 +00:00
|
|
|
inline bool updateMetrics( const QRectF& rect,
|
2024-09-17 11:57:10 +00:00
|
|
|
const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics )
|
|
|
|
{
|
|
|
|
QskHashValue hash = 13000;
|
2022-12-05 11:34:08 +00:00
|
|
|
|
2024-09-17 11:57:10 +00:00
|
|
|
hash = qHashBits( &rect, sizeof( rect ), hash );
|
|
|
|
hash = shape.hash( hash );
|
|
|
|
hash = borderMetrics.hash( hash );
|
2022-12-05 11:34:08 +00:00
|
|
|
|
2024-09-23 12:56:39 +00:00
|
|
|
return updateHash( m_metricsHash, hash );
|
2024-09-17 11:57:10 +00:00
|
|
|
}
|
2022-12-05 11:34:08 +00:00
|
|
|
|
2024-09-20 09:58:32 +00:00
|
|
|
inline bool updateColors(
|
|
|
|
const QskBoxBorderColors& borderColors, const QskGradient& gradient )
|
2022-12-22 19:27:17 +00:00
|
|
|
{
|
2024-09-17 11:57:10 +00:00
|
|
|
QskHashValue hash = 13000;
|
|
|
|
|
|
|
|
if ( borderColors.isVisible() )
|
|
|
|
hash = borderColors.hash( hash );
|
|
|
|
|
|
|
|
if ( gradient.isVisible() )
|
|
|
|
hash = gradient.hash( hash );
|
|
|
|
|
2024-09-23 12:56:39 +00:00
|
|
|
return updateHash( m_colorsHash, hash );
|
2022-12-22 19:27:17 +00:00
|
|
|
}
|
2022-12-05 11:34:08 +00:00
|
|
|
|
2024-09-17 11:57:10 +00:00
|
|
|
private:
|
2024-09-23 12:56:39 +00:00
|
|
|
inline bool updateHash( QskHashValue& value, const QskHashValue newValue ) const
|
2024-09-17 11:57:10 +00:00
|
|
|
{
|
|
|
|
if ( newValue != value )
|
|
|
|
{
|
|
|
|
value = newValue;
|
|
|
|
return true;
|
|
|
|
}
|
2022-12-05 11:34:08 +00:00
|
|
|
|
2024-09-17 11:57:10 +00:00
|
|
|
return false;
|
|
|
|
}
|
2022-12-05 11:34:08 +00:00
|
|
|
|
|
|
|
public:
|
2024-09-17 11:57:10 +00:00
|
|
|
QskHashValue m_metricsHash = 0;
|
|
|
|
QskHashValue m_colorsHash = 0;
|
2022-12-05 11:34:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
QskBoxRectangleNode::QskBoxRectangleNode()
|
2023-11-15 10:47:56 +00:00
|
|
|
: QskFillNode( *new QskBoxRectangleNodePrivate )
|
2022-12-05 11:34:08 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QskBoxRectangleNode::~QskBoxRectangleNode()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-09-17 11:57:10 +00:00
|
|
|
void QskBoxRectangleNode::updateFilling(
|
|
|
|
const QRectF& rect, const QskGradient& gradient )
|
2022-12-05 11:34:08 +00:00
|
|
|
{
|
2024-09-17 11:57:10 +00:00
|
|
|
updateFilling( rect, QskBoxShapeMetrics(), QskBoxBorderMetrics(), gradient );
|
2022-12-05 11:34:08 +00:00
|
|
|
}
|
|
|
|
|
2024-09-17 11:57:10 +00:00
|
|
|
void QskBoxRectangleNode::updateFilling( const QRectF& rect,
|
|
|
|
const QskBoxShapeMetrics& shape, const QskGradient& gradient )
|
2022-12-30 09:27:18 +00:00
|
|
|
{
|
2024-09-17 11:57:10 +00:00
|
|
|
updateFilling( rect, shape, QskBoxBorderMetrics(), gradient );
|
2022-12-30 09:27:18 +00:00
|
|
|
}
|
|
|
|
|
2024-09-17 11:57:10 +00:00
|
|
|
void QskBoxRectangleNode::updateFilling( const QRectF& rect,
|
|
|
|
const QskBoxShapeMetrics& shapeMetrics, const QskBoxBorderMetrics& borderMetrics,
|
|
|
|
const QskGradient& gradient )
|
2022-12-05 11:34:08 +00:00
|
|
|
{
|
|
|
|
Q_D( QskBoxRectangleNode );
|
|
|
|
|
2024-09-17 11:57:10 +00:00
|
|
|
if ( rect.isEmpty() || !gradient.isVisible() )
|
2022-12-05 11:34:08 +00:00
|
|
|
{
|
2024-09-17 11:57:10 +00:00
|
|
|
d->resetNode( this );
|
2022-12-05 11:34:08 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-09-17 11:57:10 +00:00
|
|
|
const auto fillGradient = gradient.effectiveGradient();
|
|
|
|
const auto shape = shapeMetrics.toAbsolute( rect.size() );
|
2022-12-05 11:34:08 +00:00
|
|
|
|
2024-09-17 11:57:10 +00:00
|
|
|
const bool coloredGeometry = hasHint( PreferColoredGeometry )
|
|
|
|
&& QskBoxRenderer::isGradientSupported( fillGradient );
|
2022-12-05 11:34:08 +00:00
|
|
|
|
2024-09-17 11:57:10 +00:00
|
|
|
const bool dirtyMetrics = d->updateMetrics( rect, shape, borderMetrics );
|
|
|
|
const bool dirtyColors = d->updateColors( QskBoxBorderColors(), fillGradient )
|
2024-09-23 12:56:39 +00:00
|
|
|
|| ( coloredGeometry != isGeometryColored() );
|
2024-09-17 11:57:10 +00:00
|
|
|
|
|
|
|
if ( dirtyMetrics || dirtyColors )
|
|
|
|
{
|
|
|
|
if ( coloredGeometry )
|
|
|
|
{
|
|
|
|
setColoring( QskFillNode::Polychrome );
|
|
|
|
|
2024-09-19 06:39:14 +00:00
|
|
|
QskBoxRenderer::setColoredFillLines( rect, shape,
|
|
|
|
borderMetrics, fillGradient, *geometry() );
|
2024-09-17 11:57:10 +00:00
|
|
|
|
|
|
|
markDirty( QSGNode::DirtyGeometry );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( fillGradient.isMonochrome() )
|
|
|
|
{
|
|
|
|
if ( dirtyColors )
|
|
|
|
setColoring( fillGradient.rgbStart() );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// dirtyMetrics: the shader also depends on rect !
|
|
|
|
setColoring( rect, fillGradient );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( dirtyMetrics )
|
|
|
|
{
|
2024-09-19 06:39:14 +00:00
|
|
|
QskBoxRenderer::setFillLines(
|
2024-09-17 11:57:10 +00:00
|
|
|
rect, shape, borderMetrics, *geometry() );
|
|
|
|
|
|
|
|
markDirty( QSGNode::DirtyGeometry );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskBoxRectangleNode::updateBorder( const QRectF& rect,
|
|
|
|
const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics,
|
|
|
|
const QskBoxBorderColors& borderColors )
|
|
|
|
{
|
|
|
|
Q_D( QskBoxRectangleNode );
|
|
|
|
|
2024-09-23 12:56:39 +00:00
|
|
|
if ( rect.isEmpty() || !qskHasBorder( borderMetrics, borderColors ) )
|
2022-12-05 11:34:08 +00:00
|
|
|
{
|
2024-09-17 11:57:10 +00:00
|
|
|
d->resetNode( this );
|
2022-12-05 11:34:08 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-09-23 12:56:39 +00:00
|
|
|
const bool coloredGeometry = hasHint( PreferColoredGeometry )
|
|
|
|
|| !borderColors.isMonochrome();
|
|
|
|
|
2024-09-17 11:57:10 +00:00
|
|
|
const bool dirtyMetrics = d->updateMetrics( rect, shape, borderMetrics );
|
2024-09-23 12:56:39 +00:00
|
|
|
const bool dirtyColors = d->updateColors( borderColors, QskGradient() )
|
|
|
|
|| ( coloredGeometry != isGeometryColored() );
|
2022-12-05 11:34:08 +00:00
|
|
|
|
2024-09-17 11:57:10 +00:00
|
|
|
if ( dirtyMetrics || dirtyColors )
|
2022-12-05 11:34:08 +00:00
|
|
|
{
|
2024-09-23 12:56:39 +00:00
|
|
|
if ( coloredGeometry )
|
|
|
|
{
|
|
|
|
setColoring( QskFillNode::Polychrome );
|
|
|
|
|
|
|
|
QskBoxRenderer::setColoredBorderLines( rect, shape,
|
|
|
|
borderMetrics, borderColors, *geometry() );
|
2022-12-05 11:34:08 +00:00
|
|
|
|
2024-09-23 12:56:39 +00:00
|
|
|
markDirty( QSGNode::DirtyGeometry );
|
|
|
|
}
|
2024-09-17 11:57:10 +00:00
|
|
|
else
|
2024-09-23 12:56:39 +00:00
|
|
|
{
|
2024-09-17 11:57:10 +00:00
|
|
|
setColoring( borderColors.left().rgbStart() );
|
|
|
|
|
2024-09-23 12:56:39 +00:00
|
|
|
if ( dirtyMetrics )
|
|
|
|
{
|
|
|
|
QskBoxRenderer::setBorderLines( rect, shape,
|
|
|
|
borderMetrics, *geometry() );
|
2024-09-17 11:57:10 +00:00
|
|
|
|
2024-09-23 12:56:39 +00:00
|
|
|
markDirty( QSGNode::DirtyGeometry );
|
|
|
|
}
|
|
|
|
}
|
2022-12-05 11:34:08 +00:00
|
|
|
}
|
2024-09-17 11:57:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QskBoxRectangleNode::updateBox( const QRectF& rect,
|
|
|
|
const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics,
|
|
|
|
const QskBoxBorderColors& borderColors, const QskGradient& gradient )
|
|
|
|
{
|
|
|
|
Q_D( QskBoxRectangleNode );
|
2022-12-05 11:34:08 +00:00
|
|
|
|
2024-09-17 11:57:10 +00:00
|
|
|
if ( rect.isEmpty() )
|
2022-12-05 11:34:08 +00:00
|
|
|
{
|
2024-09-17 11:57:10 +00:00
|
|
|
d->resetNode( this );
|
2022-12-05 11:34:08 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-09-17 11:57:10 +00:00
|
|
|
const bool hasFill = gradient.isVisible();
|
2024-09-23 12:56:39 +00:00
|
|
|
const bool hasBorder = qskHasBorder( borderMetrics, borderColors );
|
2022-12-05 11:34:08 +00:00
|
|
|
|
|
|
|
if ( hasFill && hasBorder )
|
|
|
|
{
|
2024-09-17 11:57:10 +00:00
|
|
|
const bool dirtyMetrics = d->updateMetrics( rect, shape, borderMetrics );
|
|
|
|
const bool dirtyColors = d->updateColors( borderColors, gradient );
|
|
|
|
|
|
|
|
if ( dirtyMetrics || dirtyColors )
|
2022-12-05 11:34:08 +00:00
|
|
|
{
|
2024-09-17 11:57:10 +00:00
|
|
|
/*
|
|
|
|
For monochrome border/fiiling with the same color we might be
|
|
|
|
able to do QskFillNode::Monochrome. However this is not implemeted in
|
|
|
|
QskBoxRenderer yet. TODO ...
|
|
|
|
*/
|
|
|
|
setColoring( QskFillNode::Polychrome );
|
|
|
|
|
|
|
|
auto fillGradient = gradient.effectiveGradient();
|
|
|
|
if ( !QskBoxRenderer::isGradientSupported( fillGradient ) )
|
2022-12-05 11:34:08 +00:00
|
|
|
{
|
2024-09-17 11:57:10 +00:00
|
|
|
qWarning() << "QskBoxRenderer does not support radial/conic gradients";
|
|
|
|
fillGradient.setDirection( QskGradient::Linear );
|
2022-12-05 11:34:08 +00:00
|
|
|
}
|
2023-11-15 10:47:56 +00:00
|
|
|
|
2024-09-19 06:39:14 +00:00
|
|
|
QskBoxRenderer::setColoredBorderAndFillLines( rect, shape, borderMetrics,
|
2024-09-17 11:57:10 +00:00
|
|
|
borderColors, fillGradient, *geometry() );
|
2022-12-05 11:34:08 +00:00
|
|
|
|
2024-09-17 11:57:10 +00:00
|
|
|
markDirty( QSGNode::DirtyGeometry );
|
2022-12-05 11:34:08 +00:00
|
|
|
}
|
|
|
|
}
|
2024-09-17 11:57:10 +00:00
|
|
|
else if ( hasFill )
|
2022-12-05 11:34:08 +00:00
|
|
|
{
|
2024-09-17 11:57:10 +00:00
|
|
|
updateFilling( rect, shape, borderMetrics, gradient );
|
|
|
|
}
|
|
|
|
else if ( hasBorder )
|
|
|
|
{
|
|
|
|
updateBorder( rect, shape, borderMetrics, borderColors );
|
2022-12-05 11:34:08 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-09-17 11:57:10 +00:00
|
|
|
d->resetNode( this );
|
2022-12-05 11:34:08 +00:00
|
|
|
}
|
2024-09-17 11:57:10 +00:00
|
|
|
}
|
2022-12-05 11:34:08 +00:00
|
|
|
|
2024-09-17 11:57:10 +00:00
|
|
|
bool QskBoxRectangleNode::isCombinedGeometrySupported( const QskGradient& gradient )
|
|
|
|
{
|
|
|
|
return QskBoxRenderer::isGradientSupported( gradient );
|
2022-12-05 11:34:08 +00:00
|
|
|
}
|