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
206BorderGroup *ShadowedRectangle::border() const
207{
208 return m_border.get();
209}
210
211ShadowGroup *ShadowedRectangle::shadow() const
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
239QColor ShadowedRectangle::color() const
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
257ShadowedRectangle::RenderType ShadowedRectangle::renderType() const
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.
Scene graph node for a shadowed rectangle.
bool contains(const AT &value) const const
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 setSize(const QSizeF &size)
void update()
void widthChanged()
QQuickWindow * window() const const
void setZ(qreal)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:46 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.