Kirigami2

shadowedrectangle.cpp
1 /*
2  * SPDX-FileCopyrightText: 2020 Arjen Hiemstra <[email protected]>
3  *
4  * SPDX-License-Identifier: LGPL-2.0-or-later
5  */
6 
7 #include "shadowedrectangle.h"
8 
9 #include <QQuickWindow>
10 #include <QSGRectangleNode>
11 #include <QSGRendererInterface>
12 
13 #include "scenegraph/paintedrectangleitem.h"
14 #if QT_CONFIG(opengl) || QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
15 #include "scenegraph/shadowedrectanglenode.h"
16 #endif
17 
18 BorderGroup::BorderGroup(QObject *parent)
19  : QObject(parent)
20 {
21 }
22 
23 qreal BorderGroup::width() const
24 {
25  return m_width;
26 }
27 
28 void BorderGroup::setWidth(qreal newWidth)
29 {
30  if (newWidth == m_width) {
31  return;
32  }
33 
34  m_width = newWidth;
35  Q_EMIT changed();
36 }
37 
39 {
40  return m_color;
41 }
42 
43 void BorderGroup::setColor(const QColor &newColor)
44 {
45  if (newColor == m_color) {
46  return;
47  }
48 
49  m_color = newColor;
50  Q_EMIT changed();
51 }
52 
53 ShadowGroup::ShadowGroup(QObject *parent)
54  : QObject(parent)
55 {
56 }
57 
58 qreal ShadowGroup::size() const
59 {
60  return m_size;
61 }
62 
63 void ShadowGroup::setSize(qreal newSize)
64 {
65  if (newSize == m_size) {
66  return;
67  }
68 
69  m_size = newSize;
70  Q_EMIT changed();
71 }
72 
73 qreal ShadowGroup::xOffset() const
74 {
75  return m_xOffset;
76 }
77 
78 void ShadowGroup::setXOffset(qreal newXOffset)
79 {
80  if (newXOffset == m_xOffset) {
81  return;
82  }
83 
84  m_xOffset = newXOffset;
85  Q_EMIT changed();
86 }
87 
88 qreal ShadowGroup::yOffset() const
89 {
90  return m_yOffset;
91 }
92 
93 void ShadowGroup::setYOffset(qreal newYOffset)
94 {
95  if (newYOffset == m_yOffset) {
96  return;
97  }
98 
99  m_yOffset = newYOffset;
100  Q_EMIT changed();
101 }
102 
104 {
105  return m_color;
106 }
107 
108 void ShadowGroup::setColor(const QColor &newColor)
109 {
110  if (newColor == m_color) {
111  return;
112  }
113 
114  m_color = newColor;
115  Q_EMIT changed();
116 }
117 
118 CornersGroup::CornersGroup(QObject *parent)
119  : QObject(parent)
120 {
121 }
122 
123 qreal CornersGroup::topLeft() const
124 {
125  return m_topLeft;
126 }
127 
128 void CornersGroup::setTopLeft(qreal newTopLeft)
129 {
130  if (newTopLeft == m_topLeft) {
131  return;
132  }
133 
134  m_topLeft = newTopLeft;
135  Q_EMIT changed();
136 }
137 
138 qreal CornersGroup::topRight() const
139 {
140  return m_topRight;
141 }
142 
143 void CornersGroup::setTopRight(qreal newTopRight)
144 {
145  if (newTopRight == m_topRight) {
146  return;
147  }
148 
149  m_topRight = newTopRight;
150  Q_EMIT changed();
151 }
152 
153 qreal CornersGroup::bottomLeft() const
154 {
155  return m_bottomLeft;
156 }
157 
158 void CornersGroup::setBottomLeft(qreal newBottomLeft)
159 {
160  if (newBottomLeft == m_bottomLeft) {
161  return;
162  }
163 
164  m_bottomLeft = newBottomLeft;
165  Q_EMIT changed();
166 }
167 
168 qreal CornersGroup::bottomRight() const
169 {
170  return m_bottomRight;
171 }
172 
173 void CornersGroup::setBottomRight(qreal newBottomRight)
174 {
175  if (newBottomRight == m_bottomRight) {
176  return;
177  }
178 
179  m_bottomRight = newBottomRight;
180  Q_EMIT changed();
181 }
182 
183 QVector4D CornersGroup::toVector4D(float all) const
184 {
185  return QVector4D{m_bottomRight < 0.0 ? all : m_bottomRight,
186  m_topRight < 0.0 ? all : m_topRight,
187  m_bottomLeft < 0.0 ? all : m_bottomLeft,
188  m_topLeft < 0.0 ? all : m_topLeft};
189 }
190 
191 ShadowedRectangle::ShadowedRectangle(QQuickItem *parentItem)
192  : QQuickItem(parentItem)
193  , m_border(std::make_unique<BorderGroup>())
194  , m_shadow(std::make_unique<ShadowGroup>())
195  , m_corners(std::make_unique<CornersGroup>())
196 {
197  setFlag(QQuickItem::ItemHasContents, true);
198 
199  connect(m_border.get(), &BorderGroup::changed, this, &ShadowedRectangle::update);
200  connect(m_shadow.get(), &ShadowGroup::changed, this, &ShadowedRectangle::update);
201  connect(m_corners.get(), &CornersGroup::changed, this, &ShadowedRectangle::update);
202 }
203 
204 ShadowedRectangle::~ShadowedRectangle()
205 {
206 }
207 
209 {
210  return m_border.get();
211 }
212 
214 {
215  return m_shadow.get();
216 }
217 
218 CornersGroup *ShadowedRectangle::corners() const
219 {
220  return m_corners.get();
221 }
222 
223 qreal ShadowedRectangle::radius() const
224 {
225  return m_radius;
226 }
227 
228 void ShadowedRectangle::setRadius(qreal newRadius)
229 {
230  if (newRadius == m_radius) {
231  return;
232  }
233 
234  m_radius = newRadius;
235  if (!isSoftwareRendering()) {
236  update();
237  }
238  Q_EMIT radiusChanged();
239 }
240 
242 {
243  return m_color;
244 }
245 
246 void ShadowedRectangle::setColor(const QColor &newColor)
247 {
248  if (newColor == m_color) {
249  return;
250  }
251 
252  m_color = newColor;
253  if (!isSoftwareRendering()) {
254  update();
255  }
256  Q_EMIT colorChanged();
257 }
258 
260 {
261  return m_renderType;
262 }
263 
264 void ShadowedRectangle::setRenderType(RenderType renderType)
265 {
266  if (renderType == m_renderType) {
267  return;
268  }
269  m_renderType = renderType;
270  update();
271  Q_EMIT renderTypeChanged();
272 }
273 
274 void ShadowedRectangle::componentComplete()
275 {
277 
278  checkSoftwareItem();
279 }
280 
281 bool ShadowedRectangle::isSoftwareRendering() const
282 {
283  return (window() && window()->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) || m_renderType == RenderType::Software;
284 }
285 
286 PaintedRectangleItem *ShadowedRectangle::softwareItem() const
287 {
288  return m_softwareItem;
289 }
290 
291 void ShadowedRectangle::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
292 {
293  if (change == QQuickItem::ItemSceneChange && value.window) {
294  checkSoftwareItem();
295  // TODO: only conditionally emit?
296  Q_EMIT softwareRenderingChanged();
297  }
298 }
299 
300 QSGNode *ShadowedRectangle::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *data)
301 {
302  Q_UNUSED(data);
303 
304 #if QT_CONFIG(opengl) || QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
305  auto shadowNode = static_cast<ShadowedRectangleNode *>(node);
306 
307  if (!shadowNode) {
308  shadowNode = new ShadowedRectangleNode{};
309 
310  // Cache lowPower state so we only execute the full check once.
311  static bool lowPower = QByteArrayList{"1", "true"}.contains(qgetenv("KIRIGAMI_LOWPOWER_HARDWARE").toLower());
312  if (m_renderType == RenderType::LowQuality || (m_renderType == RenderType::Auto && lowPower)) {
313  shadowNode->setShaderType(ShadowedRectangleMaterial::ShaderType::LowPower);
314  }
315  }
316 
317  shadowNode->setBorderEnabled(m_border->isEnabled());
318  shadowNode->setRect(boundingRect());
319  shadowNode->setSize(m_shadow->size());
320  shadowNode->setRadius(m_corners->toVector4D(m_radius));
321  shadowNode->setOffset(QVector2D{float(m_shadow->xOffset()), float(m_shadow->yOffset())});
322  shadowNode->setColor(m_color);
323  shadowNode->setShadowColor(m_shadow->color());
324  shadowNode->setBorderWidth(m_border->width());
325  shadowNode->setBorderColor(m_border->color());
326  shadowNode->updateGeometry();
327  return shadowNode;
328 #else
329  Q_UNUSED(node)
330  return nullptr;
331 #endif
332 }
333 
334 void ShadowedRectangle::checkSoftwareItem()
335 {
336  if (!m_softwareItem && isSoftwareRendering()) {
337  m_softwareItem = new PaintedRectangleItem{this};
338  // The software item is added as a "normal" child item, this means it
339  // will be part of the normal item sort order. Since there is no way to
340  // control the ordering of children, just make sure to have a very low Z
341  // value for the child, to force it to be the lowest item.
342  m_softwareItem->setZ(-99.0);
343 
344  auto updateItem = [this]() {
345  auto borderWidth = m_border->width();
346  auto rect = boundingRect();
347  m_softwareItem->setSize(rect.size());
348  m_softwareItem->setColor(m_color);
349  m_softwareItem->setRadius(m_radius);
350  m_softwareItem->setBorderWidth(borderWidth);
351  m_softwareItem->setBorderColor(m_border->color());
352  };
353 
354  updateItem();
355 
356  connect(this, &ShadowedRectangle::widthChanged, m_softwareItem, updateItem);
357  connect(this, &ShadowedRectangle::heightChanged, m_softwareItem, updateItem);
358  connect(this, &ShadowedRectangle::colorChanged, m_softwareItem, updateItem);
359  connect(this, &ShadowedRectangle::radiusChanged, m_softwareItem, updateItem);
360  connect(m_border.get(), &BorderGroup::changed, m_softwareItem, updateItem);
361  setFlag(QQuickItem::ItemHasContents, false);
362  }
363 }
Scene graph node for a shadowed rectangle.
qreal width
This property holds the border's width in pixels.
ShadowGroup shadow
This property holds the shadow's grouped property.
void setZ(qreal)
qreal yOffset
This property holds the shadow's offset in pixels on the Y axis.
QColor color
This property holds the shadow's color.
Grouped property for rectangle border.
virtual void componentComplete() override
bool contains(const T &value) const const
Grouped property for corner radius.
RenderType renderType
This property holds the rectangle's render mode.
qreal size
This property holds the shadow's approximate size in pixels.
void update()
Grouped property for the rectangle's shadow.
QColor color
This property holds the border's color.
BorderGroup border
This property holds the border's grouped property.
void heightChanged()
RenderType
Available rendering types for ShadowedRectangle.
void update(Part *part, const QByteArray &data, qint64 dataSize)
qreal radius
This property holds the radii of the rectangle's corners.
qreal xOffset
This property holds the shadow's offset in pixels on the X axis.
KJOBWIDGETS_EXPORT QWidget * window(KJob *job)
QColor color
This property holds the rectangle's color.
A rectangle with a border and rounded corners, rendered through QPainter.
void widthChanged()
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Feb 7 2023 04:14:24 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.