1 | /* |
2 | * SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl> |
3 | * |
4 | * SPDX-License-Identifier: LGPL-2.0-or-later |
5 | */ |
6 | |
7 | #pragma once |
8 | |
9 | #include <QQuickItem> |
10 | #include <memory> |
11 | |
12 | #include <QQmlEngine> |
13 | |
14 | class PaintedRectangleItem; |
15 | |
16 | /** |
17 | * @brief Grouped property for rectangle border. |
18 | */ |
19 | class BorderGroup : public QObject |
20 | { |
21 | Q_OBJECT |
22 | QML_ELEMENT |
23 | QML_UNCREATABLE("" ) |
24 | /** |
25 | * @brief This property holds the border's width in pixels. |
26 | * |
27 | * default: ``0``px |
28 | */ |
29 | Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY changed FINAL) |
30 | /** |
31 | * @brief This property holds the border's color. |
32 | * |
33 | * Full RGBA colors are supported. |
34 | * |
35 | * default: ``Qt::black`` |
36 | */ |
37 | Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY changed FINAL) |
38 | |
39 | public: |
40 | explicit BorderGroup(QObject *parent = nullptr); |
41 | |
42 | qreal width() const; |
43 | void setWidth(qreal newWidth); |
44 | |
45 | QColor color() const; |
46 | void setColor(const QColor &newColor); |
47 | |
48 | Q_SIGNAL void changed(); |
49 | |
50 | inline bool isEnabled() const |
51 | { |
52 | return !qFuzzyIsNull(d: m_width); |
53 | } |
54 | |
55 | private: |
56 | qreal m_width = 0.0; |
57 | QColor m_color = Qt::black; |
58 | }; |
59 | |
60 | /** |
61 | * @brief Grouped property for the rectangle's shadow. |
62 | */ |
63 | class ShadowGroup : public QObject |
64 | { |
65 | Q_OBJECT |
66 | QML_ELEMENT |
67 | QML_UNCREATABLE("" ) |
68 | /** |
69 | * @brief This property holds the shadow's approximate size in pixels. |
70 | * @note The actual shadow size can be less than this value due to falloff. |
71 | * |
72 | * default: ``0``px |
73 | */ |
74 | Q_PROPERTY(qreal size READ size WRITE setSize NOTIFY changed FINAL) |
75 | /** |
76 | * @brief This property holds the shadow's offset in pixels on the X axis. |
77 | * |
78 | * default: ``0``px |
79 | */ |
80 | Q_PROPERTY(qreal xOffset READ xOffset WRITE setXOffset NOTIFY changed FINAL) |
81 | /** |
82 | * @brief This property holds the shadow's offset in pixels on the Y axis. |
83 | * |
84 | * default: ``0``px |
85 | */ |
86 | Q_PROPERTY(qreal yOffset READ yOffset WRITE setYOffset NOTIFY changed FINAL) |
87 | /** |
88 | * @brief This property holds the shadow's color. |
89 | * |
90 | * Full RGBA colors are supported. |
91 | * |
92 | * default: ``Qt::black`` |
93 | */ |
94 | Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY changed FINAL) |
95 | |
96 | public: |
97 | explicit ShadowGroup(QObject *parent = nullptr); |
98 | |
99 | qreal size() const; |
100 | void setSize(qreal newSize); |
101 | |
102 | qreal xOffset() const; |
103 | void setXOffset(qreal newXOffset); |
104 | |
105 | qreal yOffset() const; |
106 | void setYOffset(qreal newYOffset); |
107 | |
108 | QColor color() const; |
109 | void setColor(const QColor &newShadowColor); |
110 | |
111 | Q_SIGNAL void changed(); |
112 | |
113 | private: |
114 | qreal m_size = 0.0; |
115 | qreal m_xOffset = 0.0; |
116 | qreal m_yOffset = 0.0; |
117 | QColor m_color = Qt::black; |
118 | }; |
119 | |
120 | /** |
121 | * @brief Grouped property for corner radius. |
122 | */ |
123 | class CornersGroup : public QObject |
124 | { |
125 | Q_OBJECT |
126 | QML_ELEMENT |
127 | QML_UNCREATABLE("" ) |
128 | /** |
129 | * @brief This property holds the top-left corner's radius in pixels. |
130 | * |
131 | * Setting this to ``-1`` indicates that the value should be ignored. |
132 | * |
133 | * default: ``-1``px |
134 | */ |
135 | Q_PROPERTY(qreal topLeftRadius READ topLeft WRITE setTopLeft NOTIFY changed FINAL) |
136 | |
137 | /** |
138 | * @brief This property holds the top-right corner's radius in pixels. |
139 | * |
140 | * Setting this to ``-1`` indicates that the value should be ignored. |
141 | * |
142 | * default: ``-1``px |
143 | */ |
144 | Q_PROPERTY(qreal topRightRadius READ topRight WRITE setTopRight NOTIFY changed FINAL) |
145 | |
146 | /** |
147 | * @brief This property holds the bottom-left corner's radius in pixels. |
148 | * |
149 | * Setting this to ``-1`` indicates that the value should be ignored. |
150 | * |
151 | * default: ``-1``px |
152 | */ |
153 | Q_PROPERTY(qreal bottomLeftRadius READ bottomLeft WRITE setBottomLeft NOTIFY changed FINAL) |
154 | |
155 | /** |
156 | * @brief This property holds the bottom-right corner's radius in pixels. |
157 | * |
158 | * Setting this to ``-1`` indicates that the value should be ignored. |
159 | * |
160 | * default: ``-1``px |
161 | */ |
162 | Q_PROPERTY(qreal bottomRightRadius READ bottomRight WRITE setBottomRight NOTIFY changed FINAL) |
163 | |
164 | public: |
165 | explicit CornersGroup(QObject *parent = nullptr); |
166 | |
167 | qreal topLeft() const; |
168 | void setTopLeft(qreal newTopLeft); |
169 | |
170 | qreal topRight() const; |
171 | void setTopRight(qreal newTopRight); |
172 | |
173 | qreal bottomLeft() const; |
174 | void setBottomLeft(qreal newBottomLeft); |
175 | |
176 | qreal bottomRight() const; |
177 | void setBottomRight(qreal newBottomRight); |
178 | |
179 | Q_SIGNAL void changed(); |
180 | |
181 | QVector4D toVector4D(float all) const; |
182 | |
183 | private: |
184 | float m_topLeft = -1.0; |
185 | float m_topRight = -1.0; |
186 | float m_bottomLeft = -1.0; |
187 | float m_bottomRight = -1.0; |
188 | }; |
189 | |
190 | /** |
191 | * @brief A rectangle with a shadow behind it. |
192 | * |
193 | * This item will render a rectangle, with a shadow below it. The rendering is done |
194 | * using distance fields, which provide greatly improved performance. The shadow is |
195 | * rendered outside of the item's bounds, so the item's width and height are the |
196 | * rectangle's width and height. |
197 | * |
198 | * @since 5.69 |
199 | * @since 2.12 |
200 | */ |
201 | class ShadowedRectangle : public QQuickItem |
202 | { |
203 | Q_OBJECT |
204 | QML_ELEMENT |
205 | /** |
206 | * @brief This property holds the radii of the rectangle's corners. |
207 | * |
208 | * This is the amount of rounding to apply to all of the rectangle's |
209 | * corners, in pixels. Each corner can have a different radius. |
210 | * |
211 | * default: ``0`` |
212 | * |
213 | * @see corners |
214 | */ |
215 | Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged FINAL) |
216 | |
217 | /** |
218 | * @brief This property holds the rectangle's color. |
219 | * |
220 | * Full RGBA colors are supported. |
221 | * |
222 | * default: ``Qt::white`` |
223 | */ |
224 | Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL) |
225 | |
226 | /** |
227 | * @brief This property holds the border's grouped property. |
228 | * |
229 | * Example usage: |
230 | * @code |
231 | * Kirigami.ShadowedRectangle { |
232 | * border.width: 2 |
233 | * border.color: Kirigami.Theme.textColor |
234 | * } |
235 | * @endcode |
236 | * @see BorderGroup |
237 | */ |
238 | Q_PROPERTY(BorderGroup *border READ border CONSTANT FINAL) |
239 | |
240 | /** |
241 | * @brief This property holds the shadow's grouped property. |
242 | * |
243 | * Example usage: |
244 | * @code |
245 | * Kirigami.ShadowedRectangle { |
246 | * shadow.size: 20 |
247 | * shadow.xOffset: 5 |
248 | * shadow.yOffset: 5 |
249 | * } |
250 | * @endcode |
251 | * |
252 | * @see ShadowGroup |
253 | */ |
254 | Q_PROPERTY(ShadowGroup *shadow READ shadow CONSTANT FINAL) |
255 | |
256 | /** |
257 | * @brief This property holds the corners grouped property |
258 | * |
259 | * Note that the values from this group override \property radius for the |
260 | * corner they affect. |
261 | * |
262 | * Example usage: |
263 | * @code |
264 | * Kirigami.ShadowedRectangle { |
265 | * corners.topLeftRadius: 4 |
266 | * corners.topRightRadius: 5 |
267 | * corners.bottomLeftRadius: 2 |
268 | * corners.bottomRightRadius: 10 |
269 | * @endcode |
270 | * |
271 | * @see CornersGroup |
272 | */ |
273 | Q_PROPERTY(CornersGroup *corners READ corners CONSTANT FINAL) |
274 | |
275 | /** |
276 | * @brief This property holds the rectangle's render mode. |
277 | * |
278 | * default: ``RenderType::Auto`` |
279 | * |
280 | * @see RenderType |
281 | */ |
282 | Q_PROPERTY(RenderType renderType READ renderType WRITE setRenderType NOTIFY renderTypeChanged FINAL) |
283 | |
284 | /** |
285 | * @brief This property tells whether software rendering is being used. |
286 | * |
287 | * default: ``false`` |
288 | */ |
289 | Q_PROPERTY(bool softwareRendering READ isSoftwareRendering NOTIFY softwareRenderingChanged FINAL) |
290 | |
291 | public: |
292 | ShadowedRectangle(QQuickItem *parent = nullptr); |
293 | ~ShadowedRectangle() override; |
294 | |
295 | /** |
296 | * @brief Available rendering types for ShadowedRectangle. |
297 | */ |
298 | enum RenderType { |
299 | |
300 | /** |
301 | * @brief Automatically determine the optimal rendering type. |
302 | * |
303 | * This will use the highest rendering quality possible, falling back to |
304 | * lower quality if the hardware doesn't support it. It will use software |
305 | * rendering if the QtQuick scene graph is set to use software rendering. |
306 | */ |
307 | Auto, |
308 | |
309 | /** |
310 | * @brief Use the highest rendering quality possible, even if the hardware might |
311 | * not be able to handle it normally. |
312 | */ |
313 | HighQuality, |
314 | |
315 | /** |
316 | * @brief Use the lowest rendering quality, even if the hardware could handle |
317 | * higher quality rendering. |
318 | * |
319 | * This might result in certain effects being omitted, like shadows. |
320 | */ |
321 | LowQuality, |
322 | |
323 | /** |
324 | * @brief Always use software rendering for this rectangle. |
325 | * |
326 | * Software rendering is intended as a fallback when the QtQuick scene |
327 | * graph is configured to use software rendering. It will result in |
328 | * a number of missing features, like shadows and multiple corner radii. |
329 | */ |
330 | Software |
331 | }; |
332 | Q_ENUM(RenderType) |
333 | |
334 | BorderGroup *border() const; |
335 | ShadowGroup *shadow() const; |
336 | CornersGroup *corners() const; |
337 | |
338 | qreal radius() const; |
339 | void setRadius(qreal newRadius); |
340 | Q_SIGNAL void radiusChanged(); |
341 | |
342 | QColor color() const; |
343 | void setColor(const QColor &newColor); |
344 | Q_SIGNAL void colorChanged(); |
345 | |
346 | RenderType renderType() const; |
347 | void setRenderType(RenderType renderType); |
348 | Q_SIGNAL void renderTypeChanged(); |
349 | |
350 | void componentComplete() override; |
351 | |
352 | bool isSoftwareRendering() const; |
353 | |
354 | Q_SIGNALS: |
355 | void softwareRenderingChanged(); |
356 | |
357 | protected: |
358 | PaintedRectangleItem *softwareItem() const; |
359 | void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) override; |
360 | QSGNode *updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *data) override; |
361 | |
362 | private: |
363 | void checkSoftwareItem(); |
364 | const std::unique_ptr<BorderGroup> m_border; |
365 | const std::unique_ptr<ShadowGroup> m_shadow; |
366 | const std::unique_ptr<CornersGroup> m_corners; |
367 | qreal m_radius = 0.0; |
368 | QColor m_color = Qt::white; |
369 | RenderType m_renderType = RenderType::Auto; |
370 | PaintedRectangleItem *m_softwareItem = nullptr; |
371 | }; |
372 | |