QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskBoxRectangleNode.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskBoxRectangleNode.h"
7#include "QskBoxBorderColors.h"
8#include "QskBoxBorderMetrics.h"
9#include "QskBoxRenderer.h"
10#include "QskBoxShapeMetrics.h"
11#include "QskGradient.h"
12#include "QskGradientDirection.h"
13#include "QskFillNodePrivate.h"
14
15static inline bool qskHasBorder(
16 const QskBoxBorderMetrics& metrics, const QskBoxBorderColors& colors )
17{
18 return !metrics.isNull() && colors.isVisible();
19}
20
21class QskBoxRectangleNodePrivate final : public QskFillNodePrivate
22{
23 public:
24 inline void resetNode( QskBoxRectangleNode* node )
25 {
26 m_metricsHash = m_colorsHash = 0;
27 node->resetGeometry();
28 }
29
30 inline bool updateMetrics( const QRectF& rect,
31 const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics )
32 {
33 QskHashValue hash = 13000;
34
35 hash = qHashBits( &rect, sizeof( rect ), hash );
36 hash = shape.hash( hash );
37 hash = borderMetrics.hash( hash );
38
39 return updateHash( m_metricsHash, hash );
40 }
41
42 inline bool updateColors(
43 const QskBoxBorderColors& borderColors, const QskGradient& gradient )
44 {
45 QskHashValue hash = 13000;
46
47 if ( borderColors.isVisible() )
48 hash = borderColors.hash( hash );
49
50 if ( gradient.isVisible() )
51 hash = gradient.hash( hash );
52
53 return updateHash( m_colorsHash, hash );
54 }
55
56 private:
57 inline bool updateHash( QskHashValue& value, const QskHashValue newValue ) const
58 {
59 if ( newValue != value )
60 {
61 value = newValue;
62 return true;
63 }
64
65 return false;
66 }
67
68 public:
69 QskHashValue m_metricsHash = 0;
70 QskHashValue m_colorsHash = 0;
71};
72
73QskBoxRectangleNode::QskBoxRectangleNode()
74 : QskFillNode( *new QskBoxRectangleNodePrivate )
75{
76}
77
78QskBoxRectangleNode::~QskBoxRectangleNode()
79{
80}
81
82void QskBoxRectangleNode::updateFilling( const QQuickWindow* window,
83 const QRectF& rect, const QskGradient& gradient )
84{
85 updateFilling( window, rect,
87}
88
89void QskBoxRectangleNode::updateFilling( const QQuickWindow* window,
90 const QRectF& rect, const QskBoxShapeMetrics& shape, const QskGradient& gradient )
91{
92 updateFilling( window, rect, shape, QskBoxBorderMetrics(), gradient );
93}
94
95void QskBoxRectangleNode::updateFilling( const QQuickWindow* window,
96 const QRectF& rect, const QskBoxShapeMetrics& shapeMetrics,
97 const QskBoxBorderMetrics& borderMetrics, const QskGradient& gradient )
98{
100
101 if ( rect.isEmpty() || !gradient.isVisible() )
102 {
103 d->resetNode( this );
104 return;
105 }
106
107 const auto fillGradient = QskBoxRenderer::effectiveGradient( gradient );
108 const auto shape = shapeMetrics.toAbsolute( rect.size() );
109
110 const bool coloredGeometry = hasHint( PreferColoredGeometry )
111 && QskBoxRenderer::isGradientSupported( fillGradient );
112
113 bool dirtyGeometry = d->updateMetrics( rect, shape, borderMetrics );
114 bool dirtyMaterial = d->updateColors( QskBoxBorderColors(), fillGradient );
115
116 if ( coloredGeometry != isGeometryColored() )
117 dirtyGeometry = dirtyMaterial = true;
118
119 if ( dirtyGeometry || dirtyMaterial )
120 {
121 QskBoxRenderer renderer( window );
122
123 if ( coloredGeometry )
124 {
125 setColoring( QskFillNode::Polychrome );
126
127 renderer.setColoredFillLines( rect, shape,
128 borderMetrics, fillGradient, *geometry() );
129
130 markDirty( QSGNode::DirtyGeometry );
131 }
132 else
133 {
134 setColoring( rect, fillGradient );
135
136 if ( dirtyGeometry )
137 {
138 renderer.setFillLines( rect, shape, borderMetrics, *geometry() );
139 markDirty( QSGNode::DirtyGeometry );
140 }
141 }
142 }
143}
144
145void QskBoxRectangleNode::updateBorder( const QQuickWindow* window,
146 const QRectF& rect, const QskBoxShapeMetrics& shapeMetrics,
147 const QskBoxBorderMetrics& borderMetrics, const QskBoxBorderColors& borderColors )
148{
149 Q_D( QskBoxRectangleNode );
150
151 if ( rect.isEmpty() || !qskHasBorder( borderMetrics, borderColors ) )
152 {
153 d->resetNode( this );
154 return;
155 }
156
157 const auto shape = shapeMetrics.toAbsolute( rect.size() );
158
159 const bool coloredGeometry = hasHint( PreferColoredGeometry )
160 || !borderColors.isMonochrome();
161
162 bool dirtyGeometry = d->updateMetrics( rect, shape, borderMetrics );
163 bool dirtyMaterial = d->updateColors( borderColors, QskGradient() );
164
165 if ( coloredGeometry != isGeometryColored() )
166 dirtyGeometry = dirtyMaterial = true;
167
168 if ( dirtyGeometry || dirtyMaterial )
169 {
170 QskBoxRenderer renderer( window );
171
172 if ( coloredGeometry )
173 {
174 setColoring( QskFillNode::Polychrome );
175
176 renderer.setColoredBorderLines( rect, shape,
177 borderMetrics, borderColors, *geometry() );
178
179 markDirty( QSGNode::DirtyGeometry );
180 }
181 else
182 {
183 setColoring( borderColors.left().rgbStart() );
184
185 if ( dirtyGeometry )
186 {
187 renderer.setBorderLines( rect, shape,
188 borderMetrics, *geometry() );
189
190 markDirty( QSGNode::DirtyGeometry );
191 }
192 }
193 }
194}
195
196void QskBoxRectangleNode::updateBox( const QQuickWindow* window, const QRectF& rect,
197 const QskBoxShapeMetrics& shapeMetrics, const QskBoxBorderMetrics& borderMetrics,
198 const QskBoxBorderColors& borderColors, const QskGradient& gradient )
199{
200 Q_D( QskBoxRectangleNode );
201
202 if ( rect.isEmpty() )
203 {
204 d->resetNode( this );
205 return;
206 }
207
208 const bool hasFill = gradient.isVisible();
209 const bool hasBorder = qskHasBorder( borderMetrics, borderColors );
210
211 if ( hasFill && hasBorder )
212 {
213 const auto shape = shapeMetrics.toAbsolute( rect.size() );
214
215 const bool isDirty = d->updateMetrics( rect, shape, borderMetrics )
216 || d->updateColors( borderColors, gradient ) || !isGeometryColored();
217
218 if ( isDirty )
219 {
220 /*
221 For monochrome border/filling with the same color we might be
222 able to do QskFillNode::Monochrome. However this is not implemeted in
223 QskBoxRenderer yet. TODO ...
224 */
225 setColoring( QskFillNode::Polychrome );
226
227 auto fillGradient = QskBoxRenderer::effectiveGradient( gradient );
228 if ( !QskBoxRenderer::isGradientSupported( fillGradient ) )
229 {
230 qWarning() << "QskBoxRenderer does not support radial/conic gradients";
231 fillGradient.setDirection( QskGradient::Linear );
232 }
233
234 QskBoxRenderer renderer( window );
235 renderer.setColoredBorderAndFillLines( rect, shape, borderMetrics,
236 borderColors, fillGradient, *geometry() );
237
238 markDirty( QSGNode::DirtyGeometry );
239 }
240 }
241 else if ( hasFill )
242 {
243 updateFilling( window, rect, shapeMetrics, borderMetrics, gradient );
244 }
245 else if ( hasBorder )
246 {
247 updateBorder( window, rect, shapeMetrics, borderMetrics, borderColors );
248 }
249 else
250 {
251 d->resetNode( this );
252 }
253}
254
255bool QskBoxRectangleNode::isCombinedGeometrySupported( const QskGradient& gradient )
256{
257 return QskBoxRenderer::isGradientSupported( gradient );
258}