Kirigami2

shadowedrectangle.cpp
1/*
2 * SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
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#include "scenegraph/shadowedrectanglenode.h"
15
16BorderGroup::BorderGroup(QObject *parent)
17 : QObject(parent)
18{
19}
20
21qreal BorderGroup::width() const
22{
23 return m_width;
24}
25
26void BorderGroup::setWidth(qreal newWidth)
27{
28 if (newWidth == m_width) {
29 return;
30 }
31
32 m_width = newWidth;
33 Q_EMIT changed();
34}
35
37{
38 return m_color;
39}
40
41void BorderGroup::setColor(const QColor &newColor)
42{
43 if (newColor == m_color) {
44 return;
45 }
46
47 m_color = newColor;
48 Q_EMIT changed();
49}
50
51ShadowGroup::ShadowGroup(QObject *parent)
52 : QObject(parent)
53{
54}
55
56qreal ShadowGroup::size() const
57{
58 return m_size;
59}
60
61void ShadowGroup::setSize(qreal newSize)
62{
63 if (newSize == m_size) {
64 return;
65 }
66
67 m_size = newSize;
68 Q_EMIT changed();
69}
70
71qreal ShadowGroup::xOffset() const
72{
73 return m_xOffset;
74}
75
76void ShadowGroup::setXOffset(qreal newXOffset)
77{
78 if (newXOffset == m_xOffset) {
79 return;
80 }
81
82 m_xOffset = newXOffset;
83 Q_EMIT changed();
84}
85
86qreal ShadowGroup::yOffset() const
87{
88 return m_yOffset;
89}
90
91void ShadowGroup::setYOffset(qreal newYOffset)
92{
93 if (newYOffset == m_yOffset) {
94 return;
95 }
96
97 m_yOffset = newYOffset;
98 Q_EMIT changed();
99}
100
102{
103 return m_color;
104}
105
106void ShadowGroup::setColor(const QColor &newColor)
107{
108 if (newColor == m_color) {
109 return;
110 }
111
112 m_color = newColor;
113 Q_EMIT changed();
114}
115
116CornersGroup::CornersGroup(QObject *parent)
117 : QObject(parent)
118{
119}
120
121qreal CornersGroup::topLeft() const
122{
123 return m_topLeft;
124}
125
126void CornersGroup::setTopLeft(qreal newTopLeft)
127{
128 if (newTopLeft == m_topLeft) {
129 return;
130 }
131
132 m_topLeft = newTopLeft;
133 Q_EMIT changed();
134}
135
136qreal CornersGroup::topRight() const
137{
138 return m_topRight;
139}
140
141void CornersGroup::setTopRight(qreal newTopRight)
142{
143 if (newTopRight == m_topRight) {
144 return;
145 }
146
147 m_topRight = newTopRight;
148 Q_EMIT changed();
149}
150
151qreal CornersGroup::bottomLeft() const
152{
153 return m_bottomLeft;
154}
155
156void CornersGroup::setBottomLeft(qreal newBottomLeft)
157{
158 if (newBottomLeft == m_bottomLeft) {
159 return;
160 }
161
162 m_bottomLeft = newBottomLeft;
163 Q_EMIT changed();
164}
165
166qreal CornersGroup::bottomRight() const
167{
168 return m_bottomRight;
169}
170
171void CornersGroup::setBottomRight(qreal newBottomRight)
172{
173 if (newBottomRight == m_bottomRight) {
174 return;
175 }
176
177 m_bottomRight = newBottomRight;
178 Q_EMIT changed();
179}
180
181QVector4D CornersGroup::toVector4D(float all) const
182{
183 return QVector4D{m_bottomRight < 0.0 ? all : m_bottomRight,
184 m_topRight < 0.0 ? all : m_topRight,
185 m_bottomLeft < 0.0 ? all : m_bottomLeft,
186 m_topLeft < 0.0 ? all : m_topLeft};
187}
188
189ShadowedRectangle::ShadowedRectangle(QQuickItem *parentItem)
190 : QQuickItem(parentItem)
191 , m_border(std::make_unique<BorderGroup>())
192 , m_shadow(std::make_unique<ShadowGroup>())
193 , m_corners(std::make_unique<CornersGroup>())
194{
195 setFlag(QQuickItem::ItemHasContents, true);
196
197 connect(m_border.get(), &BorderGroup::changed, this, &ShadowedRectangle::update);
198 connect(m_shadow.get(), &ShadowGroup::changed, this, &ShadowedRectangle::update);
199 connect(m_corners.get(), &CornersGroup::changed, this, &ShadowedRectangle::update);
200}
201
202ShadowedRectangle::~ShadowedRectangle()
203{
204}
205
207{
208 return m_border.get();
209}
210
212{
213 return m_shadow.get();
214}
215
216CornersGroup *ShadowedRectangle::corners() const
217{
218 return m_corners.get();
219}
220
221qreal ShadowedRectangle::radius() const
222{
223 return m_radius;
224}
225
226void ShadowedRectangle::setRadius(qreal newRadius)
227{
228 if (newRadius == m_radius) {
229 return;
230 }
231
232 m_radius = newRadius;
233 if (!isSoftwareRendering()) {
234 update();
235 }
236 Q_EMIT radiusChanged();
237}
238
240{
241 return m_color;
242}
243
244void ShadowedRectangle::setColor(const QColor &newColor)
245{
246 if (newColor == m_color) {
247 return;
248 }
249
250 m_color = newColor;
251 if (!isSoftwareRendering()) {
252 update();
253 }
254 Q_EMIT colorChanged();
255}
256
258{
259 return m_renderType;
260}
261
262void ShadowedRectangle::setRenderType(RenderType renderType)
263{
264 if (renderType == m_renderType) {
265 return;
266 }
267 m_renderType = renderType;
268 update();
269 Q_EMIT renderTypeChanged();
270}
271
272void ShadowedRectangle::componentComplete()
273{
275
276 checkSoftwareItem();
277}
278
279bool ShadowedRectangle::isSoftwareRendering() const
280{
281 return (window() && window()->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) || m_renderType == RenderType::Software;
282}
283
284PaintedRectangleItem *ShadowedRectangle::softwareItem() const
285{
286 return m_softwareItem;
287}
288
289void ShadowedRectangle::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
290{
291 if (change == QQuickItem::ItemSceneChange && value.window) {
292 checkSoftwareItem();
293 // TODO: only conditionally emit?
294 Q_EMIT softwareRenderingChanged();
295 }
296
297 QQuickItem::itemChange(change, value);
298}
299
300QSGNode *ShadowedRectangle::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *data)
301{
302 Q_UNUSED(data);
303
304 if (boundingRect().isEmpty()) {
305 delete node;
306 return nullptr;
307 }
308
309 auto shadowNode = static_cast<ShadowedRectangleNode *>(node);
310
311 if (!shadowNode) {
312 shadowNode = new ShadowedRectangleNode{};
313
314 // Cache lowPower state so we only execute the full check once.
315 static bool lowPower = QByteArrayList{"1", "true"}.contains(qgetenv("KIRIGAMI_LOWPOWER_HARDWARE").toLower());
316 if (m_renderType == RenderType::LowQuality || (m_renderType == RenderType::Auto && lowPower)) {
317 shadowNode->setShaderType(ShadowedRectangleMaterial::ShaderType::LowPower);
318 }
319 }
320
321 shadowNode->setBorderEnabled(m_border->isEnabled());
322 shadowNode->setRect(boundingRect());
323 shadowNode->setSize(m_shadow->size());
324 shadowNode->setRadius(m_corners->toVector4D(m_radius));
325 shadowNode->setOffset(QVector2D{float(m_shadow->xOffset()), float(m_shadow->yOffset())});
326 shadowNode->setColor(m_color);
327 shadowNode->setShadowColor(m_shadow->color());
328 shadowNode->setBorderWidth(m_border->width());
329 shadowNode->setBorderColor(m_border->color());
330 shadowNode->updateGeometry();
331 return shadowNode;
332}
333
334void 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);
362 }
363}
364
365#include "moc_shadowedrectangle.cpp"
Grouped property for rectangle border.
QML_ELEMENTqreal width
This property holds the border's width in pixels.
QColor color
This property holds the border's color.
Grouped property for corner radius.
A rectangle with a border and rounded corners, rendered through QPainter.
Grouped property for the rectangle's shadow.
qreal xOffset
This property holds the shadow's offset in pixels on the X axis.
QColor color
This property holds the shadow's color.
qreal yOffset
This property holds the shadow's offset in pixels on the Y axis.
QML_ELEMENTqreal size
This property holds the shadow's approximate size in pixels.
QColor color
This property holds the rectangle's color.
RenderType renderType
This property holds the rectangle's render mode.
RenderType
Available rendering types for ShadowedRectangle.
@ LowQuality
Use the lowest rendering quality, even if the hardware could handle higher quality rendering.
@ Software
Always use software rendering for this rectangle.
@ Auto
Automatically determine the optimal rendering type.
QML_ELEMENTqreal radius
This property holds the radii of the rectangle's corners.
BorderGroup * border
This property holds the border's grouped property.
ShadowGroup * shadow
This property holds the shadow's grouped property.
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual QRectF boundingRect() const const
virtual void componentComplete() override
void heightChanged()
virtual void itemChange(ItemChange change, const ItemChangeData &value)
void setFlag(Flag flag, bool enabled)
void update()
void widthChanged()
QQuickWindow * window() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:51:21 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.