Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickrectangularshadow.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qtypes.h"
5#include <private/qquickrectangularshadow_p_p.h>
6#include <private/qquickshadereffect_p.h>
7#include <private/qquickitem_p.h>
8#include <QtCore/qurl.h>
9#include <QtGui/qvector4d.h>
10
12
13/*!
14 \qmltype RectangularShadow
15 \inqmlmodule QtQuick.Effects
16 \inherits Item
17 \ingroup qtquick-effects
18 \brief Creates smoothed rectangle, suitable for example for
19 shadow and glow effects.
20
21 RectangularShadow is a rounded rectangle with blur applied.
22 The performance of RectangularShadow is much better than a general
23 one that creates blurred shadow/glow of any shaped item.
24
25 The following example shows how to add a shadow to a \l Rectangle:
26 \table 70%
27 \row
28 \li \image rectangularshadow-example-1.png
29 {Rounded rectangle with soft shadow offset to upper left}
30 \li \qml
31 import QtQuick
32 import QtQuick.Effects
33
34 ...
35 RectangularShadow {
36 anchors.fill: myRectangle
37 offset.x: -10
38 offset.y: -5
39 radius: myRectangle.radius
40 blur: 30
41 spread: 10
42 color: Qt.darker(myRectangle.color, 1.6)
43 }
44 Rectangle {
45 id: myRectangle
46 anchors.centerIn: parent
47 width: 200
48 height: 100
49 radius: 50
50 color: "#c0d0f0"
51 }
52 \endqml
53 \endtable
54
55 The API of RectangularShadow is similar to CSS box-shadow, with color,
56 offset, spread, and blur values. Additionally, RectangularShadow API
57 contains:
58
59 \list
60 \li \c real \l radius: Controls the rounding radius applied to rectangle
61 corners. Compared to CSS box-shadow, which inherits radius from the parent
62 element, RectangularShadow expects user to set it. This allows you to use
63 different radiuses and move the RectangularShadow separately from its parent
64 item.
65 \li \c bool \l cached: Allows caching the blurred shadow texture. This
66 increases memory usage while potentially improving rendering performance,
67 especially with bigger shadows that don't change dynamically.
68 \li \c item \l material: Contains the ShaderEffect element of the
69 RectangularShadow for advanced use. This allows, for example, extending the
70 effect with a custom shader.
71 \endlist
72
73 The rendering output also matches the CSS box-shadow, with few notable
74 differences. These differences exist to make the RectangularShadow as
75 high-performance as possible.
76 \list
77 \li Blurring is calculated mathematically in the shader rather than using
78 Gaussian blur, which CSS box-shadow implementations often use. This makes
79 the shadow look slightly different, especially with larger blur values.
80 \li Since Qt 6.11, RectangularShadow also supports individual corner
81 radius values. When not needed, for best performance, set only the common
82 \l radius.
83 \endlist
84
85 Here is a table with screenshots to compare the rendering output of
86 RectangularShadow and CSS box-shadow in the Chrome browser. The right-most
87 element is RectangularShadow in blur multiplied with \c {1.2}
88 (so \c 24, \c 48, \c 48) for a closer match.
89
90 \table 80%
91 \header
92 \li type
93 \li CSS box-shadow
94 \li RectangularShadow
95 \li RectangularShadow + extra blur
96 \row
97 \li offset: (0, 0) \br
98 blur: 20 \br
99 spread: 0 \br
100 \li \image rectangularshadow-css-1.png {CSS box-shadow with blur 20}
101 \li \image rectangularshadow-item-1.png {RectangularShadow with blur 20}
102 \li \image rectangularshadow-itemblur-1.png
103 {RectangularShadow with blur 24 for closer CSS match}
104 \row
105 \li offset: (-10, -20) \br
106 blur: 40 \br
107 spread: 0 \br
108 \li \image rectangularshadow-css-2.png
109 {CSS box-shadow with offset and blur 40}
110 \li \image rectangularshadow-item-2.png
111 {RectangularShadow with offset and blur 40}
112 \li \image rectangularshadow-itemblur-2.png
113 {RectangularShadow with blur 48 for closer CSS match}
114 \row
115 \li offset: (-10, -20) \br
116 blur: 40 \br
117 spread: 10 \br
118 \li \image rectangularshadow-css-3.png
119 {CSS box-shadow with offset, blur 40, and spread 10}
120 \li \image rectangularshadow-item-3.png
121 {RectangularShadow with offset, blur 40, and spread 10}
122 \li \image rectangularshadow-itemblur-3.png
123 {RectangularShadow with blur 48 and spread 10 for closer CSS
124 match}
125 \endtable
126
127
128 RectangularShadow extends the shadow size with an exact amount regarding
129 the blur amount, while some other shadows (including CSS box-shadow) have
130 a multiplier for the size. The size of the shadow item inside a
131 RectangularShadow is:
132 \badcode
133 width = rectangularShadow.width + 2 * blur + 2 * spread
134 height = rectangularShadow.height + 2 * blur + 2 * spread
135 \endcode
136
137 For example, the shadow item size of the code below is 280x180 pixels.
138 Radius or offset values do not affect the shadow item size.
139 \qml
140 RectangularShadow {
141 width: 200
142 height: 100
143 blur: 30
144 spread: 10
145 radius: 20
146 }
147 \endqml
148
149*/
150
151/*!
152 \qmlproperty bool QtQuick.Effects::RectangularShadow::antialiasing
153
154 Used to decide if the shadow should use antialiasing or not.
155 When this is \c true, a single pixel antialiasing is used even
156 when the \l blur is \c 0.
157
158 The default value is \c true.
159*/
160
161QQuickRectangularShadow::QQuickRectangularShadow(QQuickItem *parent)
162 : QQuickItem(*new QQuickRectangularShadowPrivate, parent)
163{
164 setFlag(ItemHasContents);
165}
166
167/*!
168 \qmlproperty vector2d QtQuick.Effects::RectangularShadow::offset
169
170 This property defines the position offset that is used for the shadow.
171 This offset is appended to the shadow position, relative to the
172 RectangularShadow item position.
173
174 The default value is \c {Qt.vector2d(0.0, 0.0)} (no offset).
175*/
176QVector2D QQuickRectangularShadow::offset() const
177{
178 Q_D(const QQuickRectangularShadow);
179 return d->m_offset;
180}
181
182void QQuickRectangularShadow::setOffset(const QVector2D &offset)
183{
184 Q_D(QQuickRectangularShadow);
185 if (offset == d->m_offset)
186 return;
187
188 d->m_offset = offset;
189 d->updateSizeProperties();
190 update();
191 Q_EMIT offsetChanged();
192}
193
194/*!
195 \qmlproperty color QtQuick.Effects::RectangularShadow::color
196
197 This property defines the RGBA color value that is used for the shadow.
198
199 The default value is \c {Qt.rgba(0.0, 0.0, 0.0, 1.0)} (black).
200*/
201QColor QQuickRectangularShadow::color() const
202{
203 Q_D(const QQuickRectangularShadow);
204 return d->m_color;
205}
206
207void QQuickRectangularShadow::setColor(const QColor &color)
208{
209 Q_D(QQuickRectangularShadow);
210 if (color == d->m_color)
211 return;
212
213 d->m_color = color;
214 d->updateColor();
215 update();
216 Q_EMIT colorChanged();
217}
218
219/*!
220 \qmlproperty real QtQuick.Effects::RectangularShadow::blur
221
222 This property defines how many pixels outside the item area are reached
223 by the shadow.
224
225 The value ranges from 0.0 (no blur) to inf (infinite blur).
226 The default value is \c 10.0.
227
228 \note To match with the CSS box-shadow rendering output, the optimal blur
229 amount is something like: \c {1.2 * cssBlur}
230*/
231qreal QQuickRectangularShadow::blur() const
232{
233 Q_D(const QQuickRectangularShadow);
234 return d->m_blur;
235}
236
237void QQuickRectangularShadow::setBlur(qreal blur)
238{
239 Q_D(QQuickRectangularShadow);
240 blur = qMax(qreal(0), blur);
241 if (blur == d->m_blur)
242 return;
243
244 d->m_blur = blur;
245 d->updateSizeProperties();
246 update();
247 Q_EMIT blurChanged();
248}
249
250/*!
251 \qmlproperty real QtQuick.Effects::RectangularShadow::radius
252
253 This property defines the corner radius that is used to draw a shadow with
254 rounded corners.
255
256 The value ranges from 0.0 to half of the effective width or height of
257 the item, whichever is smaller.
258
259 The default value is \c 0.
260*/
261qreal QQuickRectangularShadow::radius() const
262{
263 Q_D(const QQuickRectangularShadow);
264 return d->m_radius;
265}
266
267void QQuickRectangularShadow::setRadius(qreal radius)
268{
269 Q_D(QQuickRectangularShadow);
270 radius = qMax(qreal(0), radius);
271 if (radius == d->m_radius)
272 return;
273
274 d->m_radius = radius;
275 d->maybeSetImplicitAntialiasing();
276 d->updateSizeProperties();
277 update();
278 Q_EMIT radiusChanged();
279}
280
281/*!
282 \qmlproperty real QtQuick.Effects::RectangularShadow::spread
283
284 This property defines how much the shadow is spread (extended) in
285 pixels. This spread is appended to the shadow size, relative to the
286 RectangularShadow item size.
287
288 The value ranges from -inf to inf. The default value is \c 0.0.
289
290 \note The radius behavior with spread matches to CSS box-shadow
291 standard. So when the spread is smaller than the radius, the
292 shadow radius grows by the amount of spread. When the spread grows
293 bigger, radius grows only partially. See \l
294 {https://www.w3.org/TR/css-backgrounds-3/#shadow-shape}.
295 If the shadow radius should grow in sync when the shadow grows (like
296 with the Firefox CSS box-shadow implementation), increase the
297 RectangularShadow \c width and \c height instead of using the \c spread.
298*/
299qreal QQuickRectangularShadow::spread() const
300{
301 Q_D(const QQuickRectangularShadow);
302 return d->m_spread;
303}
304
305void QQuickRectangularShadow::setSpread(qreal spread)
306{
307 Q_D(QQuickRectangularShadow);
308 if (spread == d->m_spread)
309 return;
310
311 d->m_spread = spread;
312 d->updateSizeProperties();
313 update();
314 Q_EMIT spreadChanged();
315}
316
317/*!
318 \qmlproperty bool QtQuick.Effects::RectangularShadow::cached
319 This property allows the effect output pixels to be cached in order to
320 improve the rendering performance.
321
322 Every time the effect properties are changed, the pixels in
323 the cache must be updated. Memory consumption is increased, because an
324 extra buffer of memory is required for storing the effect output.
325
326 It is recommended to disable the cache when the source or the effect
327 properties are animated.
328
329 The default value is \c false.
330*/
331bool QQuickRectangularShadow::isCached() const
332{
333 Q_D(const QQuickRectangularShadow);
334 return d->m_cached;
335}
336
337void QQuickRectangularShadow::setCached(bool cached)
338{
339 Q_D(QQuickRectangularShadow);
340 if (cached == d->m_cached)
341 return;
342
343 d->m_cached = cached;
344 d->updateCached();
345 update();
346 Q_EMIT cachedChanged();
347}
348
349/*!
350 \qmlproperty item QtQuick.Effects::RectangularShadow::material
351
352 This property contains the \l ShaderEffect item of the shadow. You can use
353 this property to visualize the reach of the shadow, because the effect item
354 often has different position and size than the
355 RectangularShadow item, due to \l blur, \l offset and \l spread.
356
357 The material can also be replaced with a custom one. The default material
358 when not using individual corner radius is a \l ShaderEffect with the
359 following \l {ShaderEffect::}{fragmentShader}:
360
361 \badcode
362 #version 440
363
364 layout(location = 0) in vec2 texCoord;
365 layout(location = 1) in vec2 fragCoord;
366 layout(location = 0) out vec4 fragColor;
367
368 layout(std140, binding = 0) uniform buf {
369 mat4 qt_Matrix;
370 float qt_Opacity;
371 vec4 color;
372 vec3 iResolution;
373 vec2 rectSize;
374 float radius;
375 float blur;
376 };
377
378 float roundedBox(vec2 centerPos, vec2 size, float radii) {
379 return length(max(abs(centerPos) - size + radii, 0.0)) - radii;
380 }
381
382 void main()
383 {
384 float box = roundedBox(fragCoord - iResolution.xy * 0.5, rectSize, radius);
385 float a = 1.0 - smoothstep(0.0, blur, box);
386 fragColor = color * qt_Opacity * a * a;
387 }
388 \endcode
389
390 When using individual corner radius, the value of "float radius" uniform
391 is < 0 and there is an additional uniform "vec4 radius4". The corner radius
392 values order in the vec4 is top-left, top-right, bottom-left, bottom-right.
393
394 Qt Quick Effect Maker contains the RectangularShadow node that can be used
395 as a starting point for a custom material. You can directly use the exported
396 effect containing that node as a RectangularShadow material.
397 \qml
398 RectangularShadow {
399 ...
400 material: MyShadowEffect { }
401 }
402 \endqml
403
404 To return to use the default material, set the material property to \c null.
405*/
406QQuickItem *QQuickRectangularShadow::material() const
407{
408 Q_D(const QQuickRectangularShadow);
409 return d->currentMaterial();
410}
411
412void QQuickRectangularShadow::setMaterial(QQuickItem *item)
413{
414 Q_D(QQuickRectangularShadow);
415 if (item == d->m_material)
416 return;
417
418 if (item) {
419 item->setParentItem(this);
420 item->setZ(-1);
421 }
422 if (d->m_material)
423 d->m_material->setVisible(false);
424
425 d->m_material = item;
426 d->updateShaderSource();
427 update();
428 Q_EMIT materialChanged();
429}
430
431/*!
432 \since 6.11
433 \qmlproperty real QtQuick.Effects::RectangularShadow::topLeftRadius
434 This property holds the radius used to draw the top left corner.
435
436 If \l topLeftRadius is not set, \l radius will be used instead.
437 If \l topLeftRadius is zero, the corner will be sharp.
438
439 \sa radius, topRightRadius, bottomLeftRadius, bottomRightRadius
440*/
441qreal QQuickRectangularShadow::topLeftRadius() const
442{
443 Q_D(const QQuickRectangularShadow);
444 if (d->extra.isAllocated() && d->extra->isTopLeftRadiusSet)
445 return d->extra->topLeftRadius;
446 return d->m_radius;
447}
448
449void QQuickRectangularShadow::setTopLeftRadius(qreal radius)
450{
451 Q_D(QQuickRectangularShadow);
452 if (d->extra.isAllocated()
453 && d->extra->topLeftRadius == radius
454 && d->extra->isTopLeftRadiusSet) {
455 return;
456 }
457
458 d->extra.value().topLeftRadius = radius;
459 d->extra.value().isTopLeftRadiusSet = true;
460 d->maybeSetImplicitAntialiasing();
461
462 d->updateSizeProperties();
463 update();
464 emit topLeftRadiusChanged();
465}
466
467void QQuickRectangularShadow::resetTopLeftRadius()
468{
469 Q_D(QQuickRectangularShadow);
470 if (!d->extra.isAllocated())
471 return;
472 if (!d->extra->isTopLeftRadiusSet)
473 return;
474
475 d->extra->isTopLeftRadiusSet = false;
476 d->maybeSetImplicitAntialiasing();
477
478 d->updateSizeProperties();
479 update();
480 emit topLeftRadiusChanged();
481}
482
483/*!
484 \since 6.11
485 \qmlproperty real QtQuick.Effects::RectangularShadow::topRightRadius
486 This property holds the radius used to draw the top right corner.
487
488 If \l topRightRadius is not set, \l radius will be used instead.
489 If \l topRightRadius is zero, the corner will be sharp.
490
491 \sa radius, topLeftRadius, bottomLeftRadius, bottomRightRadius
492*/
493qreal QQuickRectangularShadow::topRightRadius() const
494{
495 Q_D(const QQuickRectangularShadow);
496 if (d->extra.isAllocated() && d->extra->isTopRightRadiusSet)
497 return d->extra->topRightRadius;
498 return d->m_radius;
499}
500
501void QQuickRectangularShadow::setTopRightRadius(qreal radius)
502{
503 Q_D(QQuickRectangularShadow);
504 if (d->extra.isAllocated()
505 && d->extra->topRightRadius == radius
506 && d->extra->isTopRightRadiusSet) {
507 return;
508 }
509
510 d->extra.value().topRightRadius = radius;
511 d->extra.value().isTopRightRadiusSet = true;
512 d->maybeSetImplicitAntialiasing();
513
514 d->updateSizeProperties();
515 update();
516 emit topRightRadiusChanged();
517}
518
519void QQuickRectangularShadow::resetTopRightRadius()
520{
521 Q_D(QQuickRectangularShadow);
522 if (!d->extra.isAllocated())
523 return;
524 if (!d->extra.value().isTopRightRadiusSet)
525 return;
526
527 d->extra->isTopRightRadiusSet = false;
528 d->maybeSetImplicitAntialiasing();
529
530 d->updateSizeProperties();
531 update();
532 emit topRightRadiusChanged();
533}
534
535/*!
536 \since 6.11
537 \qmlproperty real QtQuick.Effects::RectangularShadow::bottomLeftRadius
538 This property holds the radius used to draw the bottom left corner.
539
540 If \l bottomLeftRadius is not set, \l radius will be used instead.
541 If \l bottomLeftRadius is zero, the corner will be sharp.
542
543 \sa radius, topLeftRadius, topRightRadius, bottomRightRadius
544*/
545qreal QQuickRectangularShadow::bottomLeftRadius() const
546{
547 Q_D(const QQuickRectangularShadow);
548 if (d->extra.isAllocated() && d->extra->isBottomLeftRadiusSet)
549 return d->extra->bottomLeftRadius;
550 return d->m_radius;
551}
552
553void QQuickRectangularShadow::setBottomLeftRadius(qreal radius)
554{
555 Q_D(QQuickRectangularShadow);
556 if (d->extra.isAllocated()
557 && d->extra->bottomLeftRadius == radius
558 && d->extra->isBottomLeftRadiusSet) {
559 return;
560 }
561
562 d->extra.value().bottomLeftRadius = radius;
563 d->extra.value().isBottomLeftRadiusSet = true;
564 d->maybeSetImplicitAntialiasing();
565
566 d->updateSizeProperties();
567 update();
568 emit bottomLeftRadiusChanged();
569}
570
571void QQuickRectangularShadow::resetBottomLeftRadius()
572{
573 Q_D(QQuickRectangularShadow);
574 if (!d->extra.isAllocated())
575 return;
576 if (!d->extra.value().isBottomLeftRadiusSet)
577 return;
578
579 d->extra->isBottomLeftRadiusSet = false;
580 d->maybeSetImplicitAntialiasing();
581
582 d->updateSizeProperties();
583 update();
584 emit bottomLeftRadiusChanged();
585}
586
587/*!
588 \since 6.11
589 \qmlproperty real QtQuick.Effects::RectangularShadow::bottomRightRadius
590 This property holds the radius used to draw the bottom right corner.
591
592 If \l bottomRightRadius is not set, \l radius will be used instead.
593 If \l bottomRightRadius is zero, the corner will be sharp.
594
595 \sa radius, topLeftRadius, topRightRadius, bottomLeftRadius
596*/
597qreal QQuickRectangularShadow::bottomRightRadius() const
598{
599 Q_D(const QQuickRectangularShadow);
600 if (d->extra.isAllocated() && d->extra->isBottomRightRadiusSet)
601 return d->extra->bottomRightRadius;
602 return d->m_radius;
603}
604
605void QQuickRectangularShadow::setBottomRightRadius(qreal radius)
606{
607 Q_D(QQuickRectangularShadow);
608 if (d->extra.isAllocated()
609 && d->extra->bottomRightRadius == radius
610 && d->extra->isBottomRightRadiusSet) {
611 return;
612 }
613
614 d->extra.value().bottomRightRadius = radius;
615 d->extra.value().isBottomRightRadiusSet = true;
616 d->maybeSetImplicitAntialiasing();
617
618 d->updateSizeProperties();
619 update();
620 emit bottomRightRadiusChanged();
621}
622
623void QQuickRectangularShadow::resetBottomRightRadius()
624{
625 Q_D(QQuickRectangularShadow);
626 if (!d->extra.isAllocated())
627 return;
628 if (!d->extra.value().isBottomRightRadiusSet)
629 return;
630
631 d->extra->isBottomRightRadiusSet = false;
632 d->maybeSetImplicitAntialiasing();
633
634 d->updateSizeProperties();
635 update();
636 emit bottomRightRadiusChanged();
637}
638
639// *** protected ***
640
641void QQuickRectangularShadow::componentComplete()
642{
643 Q_D(QQuickRectangularShadow);
644 QQuickItem::componentComplete();
645 d->initialize();
646}
647
648void QQuickRectangularShadow::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
649{
650 Q_D(QQuickRectangularShadow);
651 QQuickItem::geometryChange(newGeometry, oldGeometry);
652 if (width() > 0 && height() > 0)
653 d->handleGeometryChange(newGeometry, oldGeometry);
654}
655
656void QQuickRectangularShadow::itemChange(ItemChange change, const ItemChangeData &value)
657{
658 Q_D(QQuickRectangularShadow);
659 d->handleItemChange(change, value);
660 QQuickItem::itemChange(change, value);
661}
662
663// *** private ***
664
665QQuickRectangularShadowPrivate::QQuickRectangularShadowPrivate()
666{
667 Q_Q(QQuickRectangularShadow);
668 m_defaultMaterial = new QQuickShaderEffect(q);
669 // Initial values to not get warnings of missing properties.
670 // Proper values are updated later.
671 m_defaultMaterial->setProperty("iResolution", QVector3D());
672 m_defaultMaterial->setProperty("rectSize", QPointF());
673 m_defaultMaterial->setProperty("color", QColorConstants::Black);
674 // Add both radius variants, shader decides which one to use
675 m_defaultMaterial->setProperty("radius", 0.0);
676 m_defaultMaterial->setProperty("radius4", QVector4D());
677 m_defaultMaterial->setProperty("blur", 10.0);
678}
679
680void QQuickRectangularShadowPrivate::updateDefaultShader()
681{
682 QUrl fs;
683 if (useIndividualRadius())
684 fs = QUrl(QStringLiteral("qrc:/data/shaders/rectangularshadow4r.frag.qsb"));
685 else
686 fs = QUrl(QStringLiteral("qrc:/data/shaders/rectangularshadow.frag.qsb"));
687 m_defaultMaterial->setFragmentShader(fs);
688}
689
690void QQuickRectangularShadowPrivate::initialize()
691{
692 Q_Q(QQuickRectangularShadow);
693 if (m_initialized)
694 return;
695 if (!q->isComponentComplete())
696 return;
697 if (!q->window())
698 return;
699 if (q->width() <= 0 || q->height() <= 0)
700 return;
701
702 m_defaultMaterial->setParentItem(q);
703 m_defaultMaterial->setZ(-1);
704 // Default to antialiased
705 setImplicitAntialiasing(true);
706
707 updateDefaultShader();
708 QUrl vs = QUrl(QStringLiteral("qrc:/data/shaders/rectangularshadow.vert.qsb"));
709 m_defaultMaterial->setVertexShader(vs);
710
711 updateShaderSource();
712 m_initialized = true;
713}
714
715void QQuickRectangularShadowPrivate::handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
716{
717 Q_UNUSED(value);
718 if (change == QQuickItem::ItemSceneChange)
719 initialize();
720}
721
722void QQuickRectangularShadowPrivate::handleGeometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
723{
724 Q_UNUSED(newGeometry);
725 Q_UNUSED(oldGeometry);
726 initialize();
727 updateSizeProperties();
728}
729
730qreal QQuickRectangularShadowPrivate::getPadding() const
731{
732 return qreal(m_blur * 2 + m_spread * 2);
733}
734
735void QQuickRectangularShadowPrivate::updateColor()
736{
737 currentMaterial()->setProperty("color", m_color);
738}
739
740void QQuickRectangularShadowPrivate::updateShaderSource()
741{
742 Q_Q(QQuickRectangularShadow);
743 if (!q->isComponentComplete())
744 return;
745
746 if (m_material)
747 m_defaultMaterial->setVisible(false);
748
749 updateSizeProperties();
750 updateColor();
751 currentMaterial()->setVisible(true);
752}
753
754void QQuickRectangularShadowPrivate::updateSizeProperties()
755{
756 Q_Q(QQuickRectangularShadow);
757
758 // Check if we should switch the shader
759 updateDefaultShader();
760
761 auto *material = currentMaterial();
762
763 const qreal padding = getPadding();
764 const qreal effectWidth = q->width() + padding;
765 const qreal effectHeight = q->height() + padding;
766
767 const qreal effectX = (q->width() - effectWidth) * 0.5 + m_offset.x();
768 const qreal effectY = (q->height() - effectHeight) * 0.5 + m_offset.y();
769 material->setX(effectX);
770 material->setY(effectY);
771 material->setWidth(effectWidth);
772 material->setHeight(effectHeight);
773
774 const qreal aa = q->antialiasing() ? 1.0 : 0.0;
775 material->setProperty("iResolution", QVector3D(effectWidth, effectHeight, 1.0));
776
777 // The shrinking ratio when the amount of blur increases
778 // so blur extends also towards inner direction.
779 const qreal blurReduction = m_blur * 1.8 + aa;
780 QPointF rectSize = QPointF((effectWidth * 0.5 - blurReduction),
781 (effectHeight * 0.5 - blurReduction));
782 material->setProperty("rectSize", rectSize);
783 if (useIndividualRadius()) {
784 const QVector4D clampedRad4 = clampedRadius4R();
785 material->setProperty("radius4", clampedRad4);
786 material->setProperty("radius", -1);
787 } else {
788 const qreal clampedRad = clampedRadius(m_radius);
789 material->setProperty("radius", clampedRad);
790 }
791 // Extend blur amount to match with how the CSS box-shadow blur behaves.
792 // and to fully utilize the item size.
793 const qreal shaderBlur = m_blur * 2.1 + aa;
794 material->setProperty("blur", shaderBlur);
795}
796
797void QQuickRectangularShadowPrivate::updateCached()
798{
799 QQuickItemPrivate *effectPrivate = QQuickItemPrivate::get(currentMaterial());
800 effectPrivate->layer()->setEnabled(m_cached);
801}
802
803qreal QQuickRectangularShadowPrivate::clampedRadius(qreal radius) const
804{
805 Q_Q(const QQuickRectangularShadow);
806 qreal maxRadius = qMin(q->width(), q->height()) * 0.5;
807 maxRadius += m_spread * 2;
808 qreal spreadRadius = radius + m_spread;
809 if (radius < m_spread && !qFuzzyIsNull(m_spread)) {
810 // CSS box-shadow has a specific math to calculate radius with spread
811 // https://www.w3.org/TR/css-backgrounds-3/#shadow-shape
812 // "the spread distance is first multiplied by the proportion 1 + (r-1)^3,
813 // where r is the ratio of the border radius to the spread distance".
814 qreal r = (radius / m_spread) - 1;
815 spreadRadius = radius + m_spread * (1 + r * r * r);
816 }
817 // Reduce the radius when the blur increases
818 const qreal blurReduce = m_blur * 0.75;
819 maxRadius -= blurReduce;
820 const qreal limitedRadius = qMax(0.0, spreadRadius - blurReduce);
821 return qMin(limitedRadius, maxRadius);
822}
823
824QVector4D QQuickRectangularShadowPrivate::clampedRadius4R() const
825{
826 Q_Q(const QQuickRectangularShadow);
827 // Clamp each corner individually
828 qreal tl = clampedRadius(q->topLeftRadius());
829 qreal tr = clampedRadius(q->topRightRadius());
830 qreal bl = clampedRadius(q->bottomLeftRadius());
831 qreal br = clampedRadius(q->bottomRightRadius());
832 return QVector4D(tl, tr, bl, br);
833}
834
835QQuickItem *QQuickRectangularShadowPrivate::currentMaterial() const
836{
837 if (m_material)
838 return m_material;
839 else
840 return m_defaultMaterial;
841}
842
843void QQuickRectangularShadowPrivate::maybeSetImplicitAntialiasing()
844{
845 bool implicitAA = (m_radius > 0);
846 if (extra.isAllocated() && !implicitAA) {
847 const auto &e = extra.value();
848 implicitAA = (e.isTopLeftRadiusSet && e.topLeftRadius > 0)
849 || (e.isTopRightRadiusSet && e.topRightRadius > 0)
850 || (e.isBottomLeftRadiusSet && e.bottomLeftRadius > 0)
851 || (e.isBottomRightRadiusSet && e.bottomRightRadius > 0);
852 }
853 setImplicitAntialiasing(implicitAA);
854}
855
856bool QQuickRectangularShadowPrivate::useIndividualRadius() const
857{
858 bool individualRadius = false;
859 if (extra.isAllocated()) {
860 const auto &e = extra.value();
861 individualRadius = e.isTopLeftRadiusSet
862 || e.isTopRightRadiusSet
863 || e.isBottomLeftRadiusSet
864 || e.isBottomRightRadiusSet;
865 }
866 return individualRadius;
867}
868
869QT_END_NAMESPACE
Combined button and popup list for selecting options.