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 class PaintedRectangleItem;
13 
14 /**
15  * Grouped property for rectangle border.
16  */
17 class BorderGroup : public QObject
18 {
19     Q_OBJECT
20     /**
21      * The width of the border in pixels.
22      *
23      * Default is 0.
24      */
25     Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY changed)
26     /**
27      * The color of the border.
28      *
29      * Full RGBA colors are supported. The default is fully opaque black.
30      */
31     Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY changed)
32 
33 public:
34     explicit BorderGroup(QObject *parent = nullptr);
35 
36     qreal width() const;
37     void setWidth(qreal newWidth);
38 
39     QColor color() const;
40     void setColor(const QColor &newColor);
41 
42     Q_SIGNAL void changed();
43 
isEnabled()44     inline bool isEnabled() const
45     {
46         return !qFuzzyIsNull(m_width);
47     }
48 
49 private:
50     qreal m_width = 0.0;
51     QColor m_color = Qt::black;
52 };
53 
54 /**
55  * Grouped property for rectangle shadow.
56  */
57 class ShadowGroup : public QObject
58 {
59     Q_OBJECT
60     /**
61      * The size of the shadow.
62      *
63      * This is the approximate size of the shadow in pixels. However, due to falloff
64      * the actual shadow size can differ. The default is 0, which means no shadow will
65      * be rendered.
66      */
67     Q_PROPERTY(qreal size READ size WRITE setSize NOTIFY changed)
68     /**
69      * Offset of the shadow on the X axis.
70      *
71      * In pixels. The default is 0.
72      */
73     Q_PROPERTY(qreal xOffset READ xOffset WRITE setXOffset NOTIFY changed)
74     /**
75      * Offset of the shadow on the Y axis.
76      *
77      * In pixels. The default is 0.
78      */
79     Q_PROPERTY(qreal yOffset READ yOffset WRITE setYOffset NOTIFY changed)
80     /**
81      * The color of the shadow.
82      *
83      * Full RGBA colors are supported. The default is fully opaque black.
84      */
85     Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY changed)
86 
87 public:
88     explicit ShadowGroup(QObject *parent = nullptr);
89 
90     qreal size() const;
91     void setSize(qreal newSize);
92 
93     qreal xOffset() const;
94     void setXOffset(qreal newXOffset);
95 
96     qreal yOffset() const;
97     void setYOffset(qreal newYOffset);
98 
99     QColor color() const;
100     void setColor(const QColor &newShadowColor);
101 
102     Q_SIGNAL void changed();
103 
104 private:
105     qreal m_size = 0.0;
106     qreal m_xOffset = 0.0;
107     qreal m_yOffset = 0.0;
108     QColor m_color = Qt::black;
109 };
110 
111 /**
112  * Grouped property for corner radius.
113  */
114 class CornersGroup : public QObject
115 {
116     Q_OBJECT
117     /**
118      * The radius of the top-left corner.
119      *
120      * In pixels. Defaults to -1, which indicates this value should not be used.
121      */
122     Q_PROPERTY(qreal topLeftRadius READ topLeft WRITE setTopLeft NOTIFY changed)
123     /**
124      * The radius of the top-right corner.
125      *
126      * In pixels. Defaults to -1, which indicates this value should not be used.
127      */
128     Q_PROPERTY(qreal topRightRadius READ topRight WRITE setTopRight NOTIFY changed)
129     /**
130      * The radius of the bottom-left corner.
131      *
132      * In pixels. Defaults to -1, which indicates this value should not be used.
133      */
134     Q_PROPERTY(qreal bottomLeftRadius READ bottomLeft WRITE setBottomLeft NOTIFY changed)
135     /**
136      * The radius of the bottom-right corner.
137      *
138      * In pixels. Defaults to -1, which indicates this value should not be used.
139      */
140     Q_PROPERTY(qreal bottomRightRadius READ bottomRight WRITE setBottomRight NOTIFY changed)
141 
142 public:
143     explicit CornersGroup(QObject *parent = nullptr);
144 
145     qreal topLeft() const;
146     void setTopLeft(qreal newTopLeft);
147 
148     qreal topRight() const;
149     void setTopRight(qreal newTopRight);
150 
151     qreal bottomLeft() const;
152     void setBottomLeft(qreal newBottomLeft);
153 
154     qreal bottomRight() const;
155     void setBottomRight(qreal newBottomRight);
156 
157     Q_SIGNAL void changed();
158 
159     QVector4D toVector4D(float all) const;
160 
161 private:
162     float m_topLeft = -1.0;
163     float m_topRight = -1.0;
164     float m_bottomLeft = -1.0;
165     float m_bottomRight = -1.0;
166 };
167 
168 /**
169  * A rectangle with a shadow.
170  *
171  * This item will render a rectangle, with a shadow below it. The rendering is done
172  * using distance fields, which provide greatly improved performance. The shadow is
173  * rendered outside of the item's bounds, so the item's width and height are the
174  * rectangle's width and height.
175  *
176  * @since 5.69 / 2.12
177  */
178 class ShadowedRectangle : public QQuickItem
179 {
180     Q_OBJECT
181     /**
182      * Corner radius of the rectangle.
183      *
184      * This is the amount of rounding to apply to all of the rectangle's
185      * corners, in pixels. Individual corners can have a different radius, see
186      * \property corners.
187      *
188      * The default is 0.
189      */
190     Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
191     /**
192      * The color of the rectangle.
193      *
194      * Full RGBA colors are supported. The default is fully opaque white.
195      */
196     Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
197     /**
198      * Border properties.
199      *
200      * \sa BorderGroup
201      */
202     Q_PROPERTY(BorderGroup *border READ border CONSTANT)
203     /**
204      * Shadow properties.
205      *
206      * \sa ShadowGroup
207      */
208     Q_PROPERTY(ShadowGroup *shadow READ shadow CONSTANT)
209     /**
210      * Corner radius.
211      *
212      * Note that the values from this group override \property radius for the
213      * corner they affect.
214      *
215      * \sa CornerGroup
216      */
217     Q_PROPERTY(CornersGroup *corners READ corners CONSTANT)
218 
219     Q_PROPERTY(bool softwareRendering READ isSoftwareRendering NOTIFY softwareRenderingChanged)
220 public:
221     ShadowedRectangle(QQuickItem *parent = nullptr);
222     ~ShadowedRectangle() override;
223 
224     BorderGroup *border() const;
225     ShadowGroup *shadow() const;
226     CornersGroup *corners() const;
227 
228     qreal radius() const;
229     void setRadius(qreal newRadius);
230     Q_SIGNAL void radiusChanged();
231 
232     QColor color() const;
233     void setColor(const QColor &newColor);
234     Q_SIGNAL void colorChanged();
235 
236     void componentComplete() override;
237 
238     bool isSoftwareRendering() const;
239 
240 Q_SIGNALS:
241     void softwareRenderingChanged();
242 
243 protected:
244     PaintedRectangleItem *softwareItem() const;
245     void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) override;
246     QSGNode *updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *data) override;
247 
248 private:
249     void checkSoftwareItem();
250     const std::unique_ptr<BorderGroup> m_border;
251     const std::unique_ptr<ShadowGroup> m_shadow;
252     const std::unique_ptr<CornersGroup> m_corners;
253     qreal m_radius = 0.0;
254     QColor m_color = Qt::white;
255     PaintedRectangleItem *m_softwareItem = nullptr;
256 };
257