Fluid 1.3.0
Material Design for QtQuick and Qml
Loading...
Searching...
No Matches
elevation.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2025-2026 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
2// SPDX-FileCopyrightText: 2024-2025 hypengw <hypengwip@gmail.com>
3// SPDX-License-Identifier: MPL-2.0
4//
5// Originally based on code by hypengw, licensed under the MIT license.
6
7#include <QQuickWindow>
8#include <QSGFlatColorMaterial>
9#include <QSGGeometry>
10#include <QSGRendererInterface>
11
12#include "elevation.h"
13#include "elevationmaterial.h"
14#include "geometry.h"
15
16// Some parameters come from:
17// https://github.com/flutter/engine/blob/3.24.3/display_list/skia/dl_sk_dispatcher.cc#L295
18// https://github.com/google/skia/blob/canvaskit/0.38.2/src/gpu/ganesh/SurfaceDrawContext.cpp#L1077
19constexpr float kAmbientAlpha = 0.039f;
20constexpr float kSpotAlpha = 0.25f;
21constexpr float kShadowLightRadius = 800.0f;
22constexpr float kShadowLightHeight = 600.0f;
23constexpr QVector3D kLightPos = { 0, -1, 1 };
24
25namespace Fluid {
26
27namespace SceneGraph {
28
29class ElevationNode : public QSGGeometryNode
30{
31public:
33 {
34 setGeometry(createShadowGeometry().release());
35 setMaterial(new ElevationMaterial{});
36 setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial);
37 }
38
39 void init(QQuickItem *item)
40 {
41 static_cast<ElevationMaterial *>(material())->init_fadeoff_texture(item->window());
42 }
43
45 {
46 // auto vertices =
47 // static_cast<ShadowVertex*>(geometry()->vertexData());
49 {
50 params.z_plane_params = QVector3D(0, 0, elevation);
51 params.light_pos = kLightPos;
53 params.radius = radius;
54 if (elevation == 0) {
56 }
58 auto c = this->color;
59 c.setAlphaF(kAmbientAlpha * this->color.alphaF());
60 params.ambient_color = c.rgba();
61 c.setAlphaF(kSpotAlpha * this->color.alphaF());
62 params.spot_color = c.rgba();
63
64 for (int i = 0; i < 4; i++) {
65 params.radius[i] = std::min<float>(params.radius[i], rect.height() / 2.0f);
66 }
67 }
68 updateShadowGeometry(geometry(), params, rect);
69
70 markDirty(QSGNode::DirtyGeometry);
71 }
72
73 double elevation;
74 QRectF rect;
75 QColor color;
76 QVector4D radius;
77};
78
79} // namespace SceneGraph
80
81/*
82 * Elevation
83 */
84
108Elevation::Elevation(QQuickItem *parentItem)
109 : QQuickItem(parentItem)
110{
111 setFlag(QQuickItem::ItemHasContents, true);
112 connect(this, &Elevation::elevationChanged, this, &Elevation::update);
113 connect(this, &Elevation::colorChanged, this, &Elevation::update);
114 connect(this, &Elevation::bottomLeftRadiusChanged, this, &Elevation::update);
115 connect(this, &Elevation::bottomRightRadiusChanged, this, &Elevation::update);
116 connect(this, &Elevation::topLeftRadiusChanged, this, &Elevation::update);
117 connect(this, &Elevation::topRightRadiusChanged, this, &Elevation::update);
118}
119
126
132{
133 return m_elevation;
134}
135
141{
142 if (!qFuzzyCompare(l, m_elevation)) {
143 m_elevation = l;
144 Q_EMIT elevationChanged(m_elevation);
145 }
146}
147
156qreal Elevation::radius() const
157{
158 if (m_topLeftRadius.has_value() || m_topRightRadius.has_value()
159 || m_bottomLeftRadius.has_value() || m_bottomRightRadius.has_value()) {
160 return std::max(
162 }
163 return m_radius;
164}
165
174void Elevation::setRadius(qreal newRadius)
175{
176 const qreal oldTL = topLeftRadius();
177 const qreal oldTR = topRightRadius();
178 const qreal oldBL = bottomLeftRadius();
179 const qreal oldBR = bottomRightRadius();
180
181 const bool changed = !qFuzzyCompare(m_radius, newRadius);
182 m_radius = newRadius;
183
184 // Clear individual overrides — setRadius resets all corners to the same value
185 m_topLeftRadius.reset();
186 m_topRightRadius.reset();
187 m_bottomLeftRadius.reset();
188 m_bottomRightRadius.reset();
189
190 if (changed)
191 Q_EMIT radiusChanged(m_radius);
192 if (!qFuzzyCompare(oldTL, m_radius))
193 Q_EMIT topLeftRadiusChanged(m_radius);
194 if (!qFuzzyCompare(oldTR, m_radius))
195 Q_EMIT topRightRadiusChanged(m_radius);
196 if (!qFuzzyCompare(oldBL, m_radius))
197 Q_EMIT bottomLeftRadiusChanged(m_radius);
198 if (!qFuzzyCompare(oldBR, m_radius))
199 Q_EMIT bottomRightRadiusChanged(m_radius);
200}
201
203{
204 return m_topLeftRadius.value_or(m_radius);
205}
206
207void Elevation::setTopLeftRadius(qreal newTopLeftRadius)
208{
209 if (qFuzzyCompare(topLeftRadius(), newTopLeftRadius))
210 return;
211
212 const qreal oldRadius = radius();
213 m_topLeftRadius = newTopLeftRadius;
214 Q_EMIT topLeftRadiusChanged(newTopLeftRadius);
215 if (!qFuzzyCompare(radius(), oldRadius))
216 Q_EMIT radiusChanged(radius());
217}
218
220{
221 return m_topRightRadius.value_or(m_radius);
222}
223
224void Elevation::setTopRightRadius(qreal newTopRightRadius)
225{
226 if (qFuzzyCompare(topRightRadius(), newTopRightRadius))
227 return;
228
229 const qreal oldRadius = radius();
230 m_topRightRadius = newTopRightRadius;
231 Q_EMIT topRightRadiusChanged(newTopRightRadius);
232 if (!qFuzzyCompare(radius(), oldRadius))
233 Q_EMIT radiusChanged(radius());
234}
235
237{
238 return m_bottomLeftRadius.value_or(m_radius);
239}
240
241void Elevation::setBottomLeftRadius(qreal newBottomLeftRadius)
242{
243 if (qFuzzyCompare(bottomLeftRadius(), newBottomLeftRadius))
244 return;
245
246 const qreal oldRadius = radius();
247 m_bottomLeftRadius = newBottomLeftRadius;
248 Q_EMIT bottomLeftRadiusChanged(newBottomLeftRadius);
249 if (!qFuzzyCompare(radius(), oldRadius))
250 Q_EMIT radiusChanged(radius());
251}
252
254{
255 return m_bottomRightRadius.value_or(m_radius);
256}
257
258void Elevation::setBottomRightRadius(qreal newBottomRightRadius)
259{
260 if (qFuzzyCompare(bottomRightRadius(), newBottomRightRadius))
261 return;
262
263 const qreal oldRadius = radius();
264 m_bottomRightRadius = newBottomRightRadius;
265 Q_EMIT bottomRightRadiusChanged(newBottomRightRadius);
266 if (!qFuzzyCompare(radius(), oldRadius))
267 Q_EMIT radiusChanged(radius());
268}
269
274QColor Elevation::color() const
275{
276 return m_color;
277}
278
283void Elevation::setColor(const QColor &newColor)
284{
285 if (newColor == m_color) {
286 return;
287 }
288
289 m_color = newColor;
290 Q_EMIT colorChanged(m_color);
291}
292
297{
298 QQuickItem::componentComplete();
299}
300
306void Elevation::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
307{
308 if (change == QQuickItem::ItemSceneChange && value.window) {
309 // checkSoftwareItem();
310 }
311
312 QQuickItem::itemChange(change, value);
313}
314
321QSGNode *Elevation::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *data)
322{
323 Q_UNUSED(data);
324
325 if (boundingRect().isEmpty()) {
326 delete node;
327 return nullptr;
328 }
329 auto shadowNode = static_cast<SceneGraph::ElevationNode *>(node);
330
331 if (!shadowNode) {
332 shadowNode = new SceneGraph::ElevationNode{};
333 shadowNode->init(this);
334 }
335 shadowNode->rect = boundingRect();
336 shadowNode->elevation = m_elevation;
337 shadowNode->radius = QVector4D{ (float)topLeftRadius(), (float)topRightRadius(),
338 (float)bottomLeftRadius(), (float)bottomRightRadius() };
339 shadowNode->color = m_color.rgb();
340 shadowNode->updateGeometry();
341 return shadowNode;
342}
343
344} // namespace Fluid
qreal topRightRadius
Definition elevation.h:25
void setColor(const QColor &newColor)
Sets the shadow color.
void setBottomLeftRadius(qreal newBottomLeftRadius)
QSGNode * updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *data) override
Updates the paint node for rendering.
void setElevation(qreal elevation)
Sets the elevation level.
void topLeftRadiusChanged(qreal topLeftRadius)
void radiusChanged(qreal radius)
void setBottomRightRadius(qreal newBottomRightRadius)
void componentComplete() override
Called when the component is complete.
void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) override
Handles item changes.
void colorChanged(QColor color)
Elevation(QQuickItem *parent=nullptr)
Constructs an Elevation item with an optional parent.
void setRadius(qreal newRadius)
Sets the radius for all corners.
void setTopLeftRadius(qreal newTopLeftRadius)
~Elevation() override
Destructor for the Elevation item.
void topRightRadiusChanged(qreal topRightRadius)
void bottomLeftRadiusChanged(qreal bottomLeftRadius)
qreal topLeftRadius
Definition elevation.h:23
void elevationChanged(qreal elevation)
void bottomRightRadiusChanged(qreal bottomRightRadius)
qreal bottomRightRadius
Definition elevation.h:29
qreal bottomLeftRadius
Definition elevation.h:27
void setTopRightRadius(qreal newTopRightRadius)
void init(QQuickItem *item)
Definition elevation.cpp:39
constexpr float kAmbientAlpha
Definition elevation.cpp:19
constexpr float kShadowLightRadius
Definition elevation.cpp:21
constexpr float kShadowLightHeight
Definition elevation.cpp:22
constexpr QVector3D kLightPos
Definition elevation.cpp:23
constexpr float kSpotAlpha
Definition elevation.cpp:20
@ DirectionalLight_ShadowFlag
Definition geometry.h:96
@ TransparentOccluder_ShadowFlag
Definition geometry.h:92
std::unique_ptr< QSGGeometry, std::default_delete< QSGGeometry > > createShadowGeometry()
Definition geometry.cpp:232
void updateShadowGeometry(QSGGeometry *geo, const ShadowParams &params, const QRectF &rect)
Definition geometry.cpp:250