1/*
2 * SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7#include "shadowedrectanglenode.h"
8#include "shadowedborderrectanglematerial.h"
9
10QColor premultiply(const QColor &color)
11{
12 return QColor::fromRgbF(r: color.redF() * color.alphaF(), //
13 g: color.greenF() * color.alphaF(),
14 b: color.blueF() * color.alphaF(),
15 a: color.alphaF());
16}
17
18ShadowedRectangleNode::ShadowedRectangleNode()
19{
20 m_geometry = new QSGGeometry{QSGGeometry::defaultAttributes_TexturedPoint2D(), 4};
21 setGeometry(m_geometry);
22
23 setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial);
24}
25
26void ShadowedRectangleNode::setBorderEnabled(bool enabled)
27{
28 // We can achieve more performant shaders by splitting the two into separate
29 // shaders. This requires separating the materials as well. So when
30 // borderWidth is increased to something where the border should be visible,
31 // switch to the with-border material. Otherwise use the no-border version.
32
33 if (enabled) {
34 if (!m_material || m_material->type() == borderlessMaterialType()) {
35 auto newMaterial = createBorderMaterial();
36 newMaterial->shaderType = m_shaderType;
37 setMaterial(newMaterial);
38 m_material = newMaterial;
39 m_rect = QRectF{};
40 markDirty(bits: QSGNode::DirtyMaterial);
41 }
42 } else {
43 if (!m_material || m_material->type() == borderMaterialType()) {
44 auto newMaterial = createBorderlessMaterial();
45 newMaterial->shaderType = m_shaderType;
46 setMaterial(newMaterial);
47 m_material = newMaterial;
48 m_rect = QRectF{};
49 markDirty(bits: QSGNode::DirtyMaterial);
50 }
51 }
52}
53
54void ShadowedRectangleNode::setRect(const QRectF &rect)
55{
56 if (rect == m_rect) {
57 return;
58 }
59
60 m_rect = rect;
61
62 QVector2D newAspect{1.0, 1.0};
63 if (m_rect.width() >= m_rect.height()) {
64 newAspect.setX(m_rect.width() / m_rect.height());
65 } else {
66 newAspect.setY(m_rect.height() / m_rect.width());
67 }
68
69 if (m_material->aspect != newAspect) {
70 m_material->aspect = newAspect;
71 markDirty(bits: QSGNode::DirtyMaterial);
72 m_aspect = newAspect;
73 }
74}
75
76void ShadowedRectangleNode::setSize(qreal size)
77{
78 auto minDimension = std::min(a: m_rect.width(), b: m_rect.height());
79 float uniformSize = (size / minDimension) * 2.0;
80
81 if (!qFuzzyCompare(p1: m_material->size, p2: uniformSize)) {
82 m_material->size = uniformSize;
83 markDirty(bits: QSGNode::DirtyMaterial);
84 m_size = size;
85 }
86}
87
88void ShadowedRectangleNode::setRadius(const QVector4D &radius)
89{
90 float minDimension = std::min(a: m_rect.width(), b: m_rect.height());
91 auto uniformRadius = QVector4D{std::min(a: radius.x() * 2.0f / minDimension, b: 1.0f),
92 std::min(a: radius.y() * 2.0f / minDimension, b: 1.0f),
93 std::min(a: radius.z() * 2.0f / minDimension, b: 1.0f),
94 std::min(a: radius.w() * 2.0f / minDimension, b: 1.0f)};
95
96 if (m_material->radius != uniformRadius) {
97 m_material->radius = uniformRadius;
98 markDirty(bits: QSGNode::DirtyMaterial);
99 m_radius = radius;
100 }
101}
102
103void ShadowedRectangleNode::setColor(const QColor &color)
104{
105 auto premultiplied = premultiply(color);
106 if (m_material->color != premultiplied) {
107 m_material->color = premultiplied;
108 markDirty(bits: QSGNode::DirtyMaterial);
109 }
110}
111
112void ShadowedRectangleNode::setShadowColor(const QColor &color)
113{
114 auto premultiplied = premultiply(color);
115 if (m_material->shadowColor != premultiplied) {
116 m_material->shadowColor = premultiplied;
117 markDirty(bits: QSGNode::DirtyMaterial);
118 }
119}
120
121void ShadowedRectangleNode::setOffset(const QVector2D &offset)
122{
123 auto minDimension = std::min(a: m_rect.width(), b: m_rect.height());
124 auto uniformOffset = offset / minDimension;
125
126 if (m_material->offset != uniformOffset) {
127 m_material->offset = uniformOffset;
128 markDirty(bits: QSGNode::DirtyMaterial);
129 m_offset = offset;
130 }
131}
132
133void ShadowedRectangleNode::setBorderWidth(qreal width)
134{
135 if (m_material->type() != borderMaterialType()) {
136 return;
137 }
138
139 auto minDimension = std::min(a: m_rect.width(), b: m_rect.height());
140 float uniformBorderWidth = width / minDimension;
141
142 auto borderMaterial = static_cast<ShadowedBorderRectangleMaterial *>(m_material);
143 if (!qFuzzyCompare(p1: borderMaterial->borderWidth, p2: uniformBorderWidth)) {
144 borderMaterial->borderWidth = uniformBorderWidth;
145 markDirty(bits: QSGNode::DirtyMaterial);
146 m_borderWidth = width;
147 }
148}
149
150void ShadowedRectangleNode::setBorderColor(const QColor &color)
151{
152 if (m_material->type() != borderMaterialType()) {
153 return;
154 }
155
156 auto borderMaterial = static_cast<ShadowedBorderRectangleMaterial *>(m_material);
157 auto premultiplied = premultiply(color);
158 if (borderMaterial->borderColor != premultiplied) {
159 borderMaterial->borderColor = premultiplied;
160 markDirty(bits: QSGNode::DirtyMaterial);
161 }
162}
163
164void ShadowedRectangleNode::setShaderType(ShadowedRectangleMaterial::ShaderType type)
165{
166 m_shaderType = type;
167}
168
169void ShadowedRectangleNode::updateGeometry()
170{
171 auto rect = m_rect;
172 if (m_shaderType == ShadowedRectangleMaterial::ShaderType::Standard) {
173 rect = rect.adjusted(xp1: -m_size * m_aspect.x(), //
174 yp1: -m_size * m_aspect.y(),
175 xp2: m_size * m_aspect.x(),
176 yp2: m_size * m_aspect.y());
177
178 auto offsetLength = m_offset.length();
179 rect = rect.adjusted(xp1: -offsetLength * m_aspect.x(), //
180 yp1: -offsetLength * m_aspect.y(),
181 xp2: offsetLength * m_aspect.x(),
182 yp2: offsetLength * m_aspect.y());
183 }
184
185 QSGGeometry::updateTexturedRectGeometry(g: m_geometry, rect, sourceRect: QRectF{0.0, 0.0, 1.0, 1.0});
186 markDirty(bits: QSGNode::DirtyGeometry);
187}
188
189ShadowedRectangleMaterial *ShadowedRectangleNode::createBorderlessMaterial()
190{
191 return new ShadowedRectangleMaterial{};
192}
193
194ShadowedBorderRectangleMaterial *ShadowedRectangleNode::createBorderMaterial()
195{
196 return new ShadowedBorderRectangleMaterial{};
197}
198
199QSGMaterialType *ShadowedRectangleNode::borderlessMaterialType()
200{
201 return &ShadowedRectangleMaterial::staticType;
202}
203
204QSGMaterialType *ShadowedRectangleNode::borderMaterialType()
205{
206 return &ShadowedBorderRectangleMaterial::staticType;
207}
208

source code of kirigami/src/scenegraph/shadowedrectanglenode.cpp