1// Copyright (C) 2016 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 "qquickrectangle_p.h"
5#include "qquickrectangle_p_p.h"
6
7#include <QtQml/qqmlinfo.h>
8
9#include <QtQuick/private/qsgcontext_p.h>
10#include <private/qsgadaptationlayer_p.h>
11
12#include <private/qqmlmetatype_p.h>
13
14#include <QtGui/qpixmapcache.h>
15#include <QtCore/qmath.h>
16#include <QtCore/qmetaobject.h>
17
18QT_BEGIN_NAMESPACE
19
20// XXX todo - should we change rectangle to draw entirely within its width/height?
21/*!
22 \internal
23 \class QQuickPen
24 \brief For specifying a pen used for drawing rectangle borders on a QQuickView
25
26 By default, the pen is invalid and nothing is drawn. You must either set a color (then the default
27 width is 1) or a width (then the default color is black).
28
29 A width of 1 indicates is a single-pixel line on the border of the item being painted.
30
31 Example:
32 \qml
33 Rectangle {
34 border.width: 2
35 border.color: "red"
36 }
37 \endqml
38*/
39
40QQuickPen::QQuickPen(QObject *parent)
41 : QObject(parent)
42 , m_width(1)
43 , m_color(Qt::black)
44 , m_aligned(true)
45 , m_valid(false)
46{
47}
48
49qreal QQuickPen::width() const
50{
51 return m_width;
52}
53
54void QQuickPen::setWidth(qreal w)
55{
56 if (m_width == w && m_valid)
57 return;
58
59 m_width = w;
60 m_valid = m_color.alpha() && (qRound(d: m_width) >= 1 || (!m_aligned && m_width > 0));
61 static_cast<QQuickItem*>(parent())->update();
62 emit widthChanged();
63}
64
65QColor QQuickPen::color() const
66{
67 return m_color;
68}
69
70void QQuickPen::setColor(const QColor &c)
71{
72 m_color = c;
73 m_valid = m_color.alpha() && (qRound(d: m_width) >= 1 || (!m_aligned && m_width > 0));
74 static_cast<QQuickItem*>(parent())->update();
75 emit colorChanged();
76}
77
78bool QQuickPen::pixelAligned() const
79{
80 return m_aligned;
81}
82
83void QQuickPen::setPixelAligned(bool aligned)
84{
85 if (aligned == m_aligned)
86 return;
87 m_aligned = aligned;
88 m_valid = m_color.alpha() && (qRound(d: m_width) >= 1 || (!m_aligned && m_width > 0));
89 static_cast<QQuickItem*>(parent())->update();
90 emit pixelAlignedChanged();
91}
92
93bool QQuickPen::isValid() const
94{
95 return m_valid;
96}
97
98/*!
99 \qmltype GradientStop
100 \nativetype QQuickGradientStop
101 \inqmlmodule QtQuick
102 \ingroup qtquick-visual-utility
103 \brief Defines the color at a position in a Gradient.
104
105 \sa Gradient
106*/
107
108/*!
109 \qmlproperty real QtQuick::GradientStop::position
110 \qmlproperty color QtQuick::GradientStop::color
111
112 The position and color properties describe the color used at a given
113 position in a gradient, as represented by a gradient stop.
114
115 The default position is 0.0; the default color is black.
116
117 \sa Gradient
118*/
119QQuickGradientStop::QQuickGradientStop(QObject *parent)
120 : QObject(parent)
121{
122}
123
124qreal QQuickGradientStop::position() const
125{
126 return m_position;
127}
128
129void QQuickGradientStop::setPosition(qreal position)
130{
131 m_position = position; updateGradient();
132}
133
134QColor QQuickGradientStop::color() const
135{
136 return m_color;
137}
138
139void QQuickGradientStop::setColor(const QColor &color)
140{
141 m_color = color; updateGradient();
142}
143
144void QQuickGradientStop::updateGradient()
145{
146 if (QQuickGradient *grad = qobject_cast<QQuickGradient*>(object: parent()))
147 grad->doUpdate();
148}
149
150/*!
151 \qmltype Gradient
152 \nativetype QQuickGradient
153 \inqmlmodule QtQuick
154 \ingroup qtquick-visual-utility
155 \brief Defines a gradient fill.
156
157 A gradient is defined by two or more colors, which will be blended seamlessly.
158
159 The colors are specified as a set of GradientStop child items, each of
160 which defines a position on the gradient from 0.0 to 1.0 and a color.
161 The position of each GradientStop is defined by setting its
162 \l{GradientStop::}{position} property; its color is defined using its
163 \l{GradientStop::}{color} property.
164
165 A gradient without any gradient stops is rendered as a solid white fill.
166
167 Note that this item is not a visual representation of a gradient. To display a
168 gradient, use a visual item (like \l Rectangle) which supports the use
169 of gradients.
170
171 \section1 Example Usage
172
173 \div {class="float-right"}
174 \inlineimage qml-gradient.png
175 \enddiv
176
177 The following example declares a \l Rectangle item with a gradient starting
178 with red, blending to yellow at one third of the height of the rectangle,
179 and ending with green:
180
181 \snippet qml/gradient.qml code
182
183 \clearfloat
184 \section1 Performance and Limitations
185
186 Calculating gradients can be computationally expensive compared to the use
187 of solid color fills or images. Consider using gradients for static items
188 in a user interface.
189
190 Since Qt 5.12, vertical and horizontal linear gradients can be applied to items.
191 If you need to apply angled gradients, a combination of rotation and clipping
192 can be applied to the relevant items. Alternatively, consider using
193 QtQuick.Shapes::LinearGradient or QtGraphicalEffects::LinearGradient. These
194 approaches can all introduce additional performance requirements for your application.
195
196 The use of animations involving gradient stops may not give the desired
197 result. An alternative way to animate gradients is to use pre-generated
198 images or SVG drawings containing gradients.
199
200 \sa GradientStop
201*/
202
203/*!
204 \qmlproperty list<GradientStop> QtQuick::Gradient::stops
205 \qmldefault
206
207 This property holds the gradient stops describing the gradient.
208
209 By default, this property contains an empty list.
210
211 To set the gradient stops, define them as children of the Gradient.
212*/
213QQuickGradient::QQuickGradient(QObject *parent)
214: QObject(parent)
215{
216}
217
218QQuickGradient::~QQuickGradient()
219{
220}
221
222QQmlListProperty<QQuickGradientStop> QQuickGradient::stops()
223{
224 return QQmlListProperty<QQuickGradientStop>(this, &m_stops);
225}
226
227/*!
228 \qmlproperty enumeration QtQuick::Gradient::orientation
229 \since 5.12
230
231 Set this property to define the direction of the gradient.
232
233 \value Gradient.Vertical a vertical gradient
234 \value Gradient.Horizontal a horizontal gradient
235
236 The default is Gradient.Vertical.
237*/
238void QQuickGradient::setOrientation(Orientation orientation)
239{
240 if (m_orientation == orientation)
241 return;
242
243 m_orientation = orientation;
244 emit orientationChanged();
245 emit updated();
246}
247
248QGradientStops QQuickGradient::gradientStops() const
249{
250 QGradientStops stops;
251 for (int i = 0; i < m_stops.size(); ++i){
252 int j = 0;
253 while (j < stops.size() && stops.at(i: j).first < m_stops[i]->position())
254 j++;
255 stops.insert(i: j, t: QGradientStop(m_stops.at(i)->position(), m_stops.at(i)->color()));
256 }
257 return stops;
258}
259
260void QQuickGradient::doUpdate()
261{
262 emit updated();
263}
264
265int QQuickRectanglePrivate::doUpdateSlotIdx = -1;
266
267void QQuickRectanglePrivate::maybeSetImplicitAntialiasing()
268{
269 bool implicitAA = (radius > 0);
270 if (extraRectangle.isAllocated() && !implicitAA) {
271 const auto &extra = extraRectangle.value();
272 implicitAA = (extra.isTopLeftRadiusSet && extra.topLeftRadius > 0)
273 || (extra.isTopRightRadiusSet && extra.topRightRadius > 0)
274 || (extra.isBottomLeftRadiusSet && extra.bottomLeftRadius > 0)
275 || (extra.isBottomRightRadiusSet && extra.bottomRightRadius > 0);
276 }
277 setImplicitAntialiasing(implicitAA);
278}
279/*!
280 \qmltype Rectangle
281 \nativetype QQuickRectangle
282 \inqmlmodule QtQuick
283 \inherits Item
284 \ingroup qtquick-visual
285 \brief Paints a filled rectangle with an optional border.
286
287 Rectangle items are used to fill areas with solid color or gradients, and/or
288 to provide a rectangular border.
289
290 \section1 Appearance
291
292 Each Rectangle item is painted using either a solid fill color, specified using
293 the \l color property, or a gradient, defined using a Gradient type and set
294 using the \l gradient property. If both a color and a gradient are specified,
295 the gradient is used.
296
297 You can add an optional border to a rectangle with its own color and thickness
298 by setting the \l border.color and \l border.width properties. Set the color
299 to "transparent" to paint a border without a fill color.
300
301 You can also create rounded rectangles using the \l radius property. Since this
302 introduces curved edges to the corners of a rectangle, it may be appropriate to
303 set the \l Item::antialiasing property to improve its appearance. To set the
304 radii individually for different corners, you can use the properties
305 \l topLeftRadius, \l topRightRadius, \l bottomLeftRadius and
306 \l bottomRightRadius.
307
308 \section1 Example Usage
309
310 \div {class="float-right"}
311 \inlineimage declarative-rect.png
312 \enddiv
313
314 The following example shows the effects of some of the common properties on a
315 Rectangle item, which in this case is used to create a square:
316
317 \snippet qml/rectangle/rectangle.qml document
318
319 \clearfloat
320 \section1 Performance
321
322 Using the \l Item::antialiasing property improves the appearance of a rounded rectangle at
323 the cost of rendering performance. You should consider unsetting this property
324 for rectangles in motion, and only set it when they are stationary.
325
326 \sa Image
327*/
328
329QQuickRectangle::QQuickRectangle(QQuickItem *parent)
330: QQuickItem(*(new QQuickRectanglePrivate), parent)
331{
332 setFlag(flag: ItemHasContents);
333#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
334 setAcceptTouchEvents(false);
335#endif
336}
337
338void QQuickRectangle::doUpdate()
339{
340 update();
341}
342
343/*!
344 \qmlproperty bool QtQuick::Rectangle::antialiasing
345
346 Used to decide if the Rectangle should use antialiasing or not.
347 \l {Antialiasing} provides information on the performance implications
348 of this property.
349
350 The default is true for Rectangles with a radius, and false otherwise.
351*/
352
353/*!
354 \qmlpropertygroup QtQuick::Rectangle::border
355 \qmlproperty int QtQuick::Rectangle::border.width
356 \qmlproperty color QtQuick::Rectangle::border.color
357 \qmlproperty bool QtQuick::Rectangle::border.pixelAligned
358
359 The width and color used to draw the border of the rectangle.
360
361 A width of 1 creates a thin line. For no line, use a width of 0 or a transparent color.
362
363 \note The width of the rectangle's border does not affect the geometry of the
364 rectangle itself or its position relative to other items if anchors are used.
365
366 The border is rendered within the rectangle's boundaries.
367
368 If \c pixelAligned is \c true (the default), the rendered border width is rounded to a whole
369 number of pixels, after device pixel ratio scaling. Setting \c pixelAligned to \c false will
370 allow fractional border widths, which may be desirable when \c antialiasing is enabled.
371*/
372QQuickPen *QQuickRectangle::border()
373{
374 Q_D(QQuickRectangle);
375 if (!d->pen) {
376 d->pen = new QQuickPen;
377 QQml_setParent_noEvent(object: d->pen, parent: this);
378 }
379 return d->pen;
380}
381
382/*!
383 \qmlproperty var QtQuick::Rectangle::gradient
384
385 The gradient to use to fill the rectangle.
386
387 This property allows for the construction of simple vertical or horizontal gradients.
388 Other gradients may be formed by adding rotation to the rectangle.
389
390 \div {class="float-left"}
391 \inlineimage declarative-rect_gradient.png
392 \enddiv
393
394 \snippet qml/rectangle/rectangle-gradient.qml rectangles
395 \clearfloat
396
397 The property also accepts gradient presets from QGradient::Preset. Note however
398 that due to Rectangle only supporting simple vertical or horizontal gradients,
399 any preset with an unsupported angle will revert to the closest representation.
400
401 \snippet qml/rectangle/rectangle-gradient.qml presets
402 \clearfloat
403
404 If both a gradient and a color are specified, the gradient will be used.
405
406 \sa Gradient, color
407*/
408QJSValue QQuickRectangle::gradient() const
409{
410 Q_D(const QQuickRectangle);
411 return d->gradient;
412}
413
414void QQuickRectangle::setGradient(const QJSValue &gradient)
415{
416 Q_D(QQuickRectangle);
417 if (d->gradient.equals(other: gradient))
418 return;
419
420 static int updatedSignalIdx = QMetaMethod::fromSignal(signal: &QQuickGradient::updated).methodIndex();
421 if (d->doUpdateSlotIdx < 0)
422 d->doUpdateSlotIdx = QQuickRectangle::staticMetaObject.indexOfSlot(slot: "doUpdate()");
423
424 if (auto oldGradient = qobject_cast<QQuickGradient*>(object: d->gradient.toQObject()))
425 QMetaObject::disconnect(sender: oldGradient, signal_index: updatedSignalIdx, receiver: this, method_index: d->doUpdateSlotIdx);
426
427 if (gradient.isQObject()) {
428 if (auto newGradient = qobject_cast<QQuickGradient*>(object: gradient.toQObject())) {
429 d->gradient = gradient;
430 QMetaObject::connect(sender: newGradient, signal_index: updatedSignalIdx, receiver: this, method_index: d->doUpdateSlotIdx);
431 } else {
432 qmlWarning(me: this) << "Can't assign "
433 << QQmlMetaType::prettyTypeName(object: gradient.toQObject()) << " to gradient property";
434 d->gradient = QJSValue();
435 }
436 } else if (gradient.isNumber() || gradient.isString()) {
437 static const QMetaEnum gradientPresetMetaEnum = QMetaEnum::fromType<QGradient::Preset>();
438 Q_ASSERT(gradientPresetMetaEnum.isValid());
439
440 QGradient result;
441
442 // This code could simply use gradient.toVariant().convert<QGradient::Preset>(),
443 // but QTBUG-76377 prevents us from doing error checks. So we need to
444 // do them manually. Also, NumPresets cannot be used.
445
446 if (gradient.isNumber()) {
447 const auto preset = QGradient::Preset(gradient.toInt());
448 if (preset != QGradient::NumPresets && gradientPresetMetaEnum.valueToKey(value: preset))
449 result = QGradient(preset);
450 } else if (gradient.isString()) {
451 const auto presetName = gradient.toString();
452 if (presetName != QLatin1String("NumPresets")) {
453 bool ok;
454 const auto presetInt = gradientPresetMetaEnum.keyToValue(qPrintable(presetName), ok: &ok);
455 if (ok)
456 result = QGradient(QGradient::Preset(presetInt));
457 }
458 }
459
460 if (result.type() != QGradient::NoGradient) {
461 d->gradient = gradient;
462 } else {
463 qmlWarning(me: this) << "No such gradient preset '" << gradient.toString() << "'";
464 d->gradient = QJSValue();
465 }
466 } else if (gradient.isNull() || gradient.isUndefined()) {
467 d->gradient = gradient;
468 } else {
469 qmlWarning(me: this) << "Unknown gradient type. Expected int, string, or Gradient";
470 d->gradient = QJSValue();
471 }
472
473 update();
474}
475
476void QQuickRectangle::resetGradient()
477{
478 setGradient(QJSValue());
479}
480
481/*!
482 \qmlproperty real QtQuick::Rectangle::radius
483 This property holds the corner radius used to draw a rounded rectangle.
484
485 If radius is non-zero, the rectangle will be painted as a rounded rectangle,
486 otherwise it will be painted as a normal rectangle. Individual corner radii
487 can be set as well (see below). These values will override \l radius. If
488 they are unset (by setting them to \c undefined), \l radius will be used instead.
489
490 \sa topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius
491*/
492qreal QQuickRectangle::radius() const
493{
494 Q_D(const QQuickRectangle);
495 return d->radius;
496}
497
498void QQuickRectangle::setRadius(qreal radius)
499{
500 Q_D(QQuickRectangle);
501 if (d->radius == radius)
502 return;
503
504 d->radius = radius;
505 d->maybeSetImplicitAntialiasing();
506
507 update();
508 emit radiusChanged();
509
510 if (d->extraRectangle.isAllocated()) {
511 if (!d->extraRectangle->isTopLeftRadiusSet)
512 emit topLeftRadiusChanged();
513 if (!d->extraRectangle->isTopRightRadiusSet)
514 emit topRightRadiusChanged();
515 if (!d->extraRectangle->isBottomLeftRadiusSet)
516 emit bottomLeftRadiusChanged();
517 if (!d->extraRectangle->isBottomRightRadiusSet)
518 emit bottomRightRadiusChanged();
519 } else {
520 emit topLeftRadiusChanged();
521 emit topRightRadiusChanged();
522 emit bottomLeftRadiusChanged();
523 emit bottomRightRadiusChanged();
524 }
525}
526
527/*!
528 \since 6.7
529 \qmlproperty real QtQuick::Rectangle::topLeftRadius
530 This property holds the radius used to draw the top left corner.
531
532 If \l topLeftRadius is not set, \l radius will be used instead.
533 If \l topLeftRadius is zero, the corner will be sharp.
534
535 \sa radius, topRightRadius, bottomLeftRadius, bottomRightRadius
536*/
537qreal QQuickRectangle::topLeftRadius() const
538{
539 Q_D(const QQuickRectangle);
540 if (d->extraRectangle.isAllocated() && d->extraRectangle->isTopLeftRadiusSet)
541 return d->extraRectangle->topLeftRadius;
542 return d->radius;
543}
544
545void QQuickRectangle::setTopLeftRadius(qreal radius)
546{
547 Q_D(QQuickRectangle);
548 if (d->extraRectangle.isAllocated()
549 && d->extraRectangle->topLeftRadius == radius
550 && d->extraRectangle->isTopLeftRadiusSet) {
551 return;
552 }
553
554 d->extraRectangle.value().topLeftRadius = radius;
555 d->extraRectangle.value().isTopLeftRadiusSet = true;
556 d->maybeSetImplicitAntialiasing();
557
558 update();
559 emit topLeftRadiusChanged();
560}
561
562void QQuickRectangle::resetTopLeftRadius()
563{
564 Q_D(QQuickRectangle);
565 if (!d->extraRectangle.isAllocated())
566 return;
567 if (!d->extraRectangle->isTopLeftRadiusSet)
568 return;
569
570 d->extraRectangle->isTopLeftRadiusSet = false;
571 d->maybeSetImplicitAntialiasing();
572
573 update();
574 emit topLeftRadiusChanged();
575}
576
577/*!
578 \since 6.7
579 \qmlproperty real QtQuick::Rectangle::topRightRadius
580 This property holds the radius used to draw the top right corner.
581
582 If \l topRightRadius is not set, \l radius will be used instead.
583 If \l topRightRadius is zero, the corner will be sharp.
584
585 \sa radius, topLeftRadius, bottomLeftRadius, bottomRightRadius
586*/
587qreal QQuickRectangle::topRightRadius() const
588{
589 Q_D(const QQuickRectangle);
590 if (d->extraRectangle.isAllocated() && d->extraRectangle->isTopRightRadiusSet)
591 return d->extraRectangle->topRightRadius;
592 return d->radius;
593}
594
595void QQuickRectangle::setTopRightRadius(qreal radius)
596{
597 Q_D(QQuickRectangle);
598 if (d->extraRectangle.isAllocated()
599 && d->extraRectangle->topRightRadius == radius
600 && d->extraRectangle->isTopRightRadiusSet) {
601 return;
602 }
603
604 d->extraRectangle.value().topRightRadius = radius;
605 d->extraRectangle.value().isTopRightRadiusSet = true;
606 d->maybeSetImplicitAntialiasing();
607
608 update();
609 emit topRightRadiusChanged();
610}
611
612void QQuickRectangle::resetTopRightRadius()
613{
614 Q_D(QQuickRectangle);
615 if (!d->extraRectangle.isAllocated())
616 return;
617 if (!d->extraRectangle.value().isTopRightRadiusSet)
618 return;
619
620 d->extraRectangle->isTopRightRadiusSet = false;
621 d->maybeSetImplicitAntialiasing();
622
623 update();
624 emit topRightRadiusChanged();
625}
626
627/*!
628 \since 6.7
629 \qmlproperty real QtQuick::Rectangle::bottomLeftRadius
630 This property holds the radius used to draw the bottom left corner.
631
632 If \l bottomLeftRadius is not set, \l radius will be used instead.
633 If \l bottomLeftRadius is zero, the corner will be sharp.
634
635 \sa radius, topLeftRadius, topRightRadius, bottomRightRadius
636*/
637qreal QQuickRectangle::bottomLeftRadius() const
638{
639 Q_D(const QQuickRectangle);
640 if (d->extraRectangle.isAllocated() && d->extraRectangle->isBottomLeftRadiusSet)
641 return d->extraRectangle->bottomLeftRadius;
642 return d->radius;
643}
644
645void QQuickRectangle::setBottomLeftRadius(qreal radius)
646{
647 Q_D(QQuickRectangle);
648 if (d->extraRectangle.isAllocated()
649 && d->extraRectangle->bottomLeftRadius == radius
650 && d->extraRectangle->isBottomLeftRadiusSet) {
651 return;
652 }
653
654 d->extraRectangle.value().bottomLeftRadius = radius;
655 d->extraRectangle.value().isBottomLeftRadiusSet = true;
656 d->maybeSetImplicitAntialiasing();
657
658 update();
659 emit bottomLeftRadiusChanged();
660}
661
662void QQuickRectangle::resetBottomLeftRadius()
663{
664 Q_D(QQuickRectangle);
665 if (!d->extraRectangle.isAllocated())
666 return;
667 if (!d->extraRectangle.value().isBottomLeftRadiusSet)
668 return;
669
670 d->extraRectangle->isBottomLeftRadiusSet = false;
671 d->maybeSetImplicitAntialiasing();
672
673 update();
674 emit bottomLeftRadiusChanged();
675}
676
677/*!
678 \since 6.7
679 \qmlproperty real QtQuick::Rectangle::bottomRightRadius
680 This property holds the radius used to draw the bottom right corner.
681
682 If \l bottomRightRadius is not set, \l radius will be used instead.
683 If \l bottomRightRadius is zero, the corner will be sharp.
684
685 \sa radius, topLeftRadius, topRightRadius, bottomLeftRadius
686*/
687qreal QQuickRectangle::bottomRightRadius() const
688{
689 Q_D(const QQuickRectangle);
690 if (d->extraRectangle.isAllocated() && d->extraRectangle->isBottomRightRadiusSet)
691 return d->extraRectangle->bottomRightRadius;
692 return d->radius;
693}
694
695void QQuickRectangle::setBottomRightRadius(qreal radius)
696{
697 Q_D(QQuickRectangle);
698 if (d->extraRectangle.isAllocated()
699 && d->extraRectangle->bottomRightRadius == radius
700 && d->extraRectangle->isBottomRightRadiusSet) {
701 return;
702 }
703
704 d->extraRectangle.value().bottomRightRadius = radius;
705 d->extraRectangle.value().isBottomRightRadiusSet = true;
706 d->maybeSetImplicitAntialiasing();
707
708 update();
709 emit bottomRightRadiusChanged();
710}
711
712void QQuickRectangle::resetBottomRightRadius()
713{
714 Q_D(QQuickRectangle);
715 if (!d->extraRectangle.isAllocated())
716 return;
717 if (!d->extraRectangle.value().isBottomRightRadiusSet)
718 return;
719
720 d->extraRectangle->isBottomRightRadiusSet = false;
721 d->maybeSetImplicitAntialiasing();
722
723 update();
724 emit bottomRightRadiusChanged();
725}
726
727/*!
728 \qmlproperty color QtQuick::Rectangle::color
729 This property holds the color used to fill the rectangle.
730
731 The default color is white.
732
733 \div {class="float-right"}
734 \inlineimage rect-color.png
735 \enddiv
736
737 The following example shows rectangles with colors specified
738 using hexadecimal and named color notation:
739
740 \snippet qml/rectangle/rectangle-colors.qml rectangles
741
742 \clearfloat
743 If both a gradient and a color are specified, the gradient will be used.
744
745 \sa gradient
746*/
747QColor QQuickRectangle::color() const
748{
749 Q_D(const QQuickRectangle);
750 return d->color;
751}
752
753void QQuickRectangle::setColor(const QColor &c)
754{
755 Q_D(QQuickRectangle);
756 if (d->color == c)
757 return;
758
759 d->color = c;
760 update();
761 emit colorChanged();
762}
763
764QSGNode *QQuickRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
765{
766 Q_UNUSED(data);
767 Q_D(QQuickRectangle);
768
769 if (width() <= 0 || height() <= 0
770 || (d->gradient.isUndefined() && d->color.alpha() == 0 && (!d->pen || d->pen->width() == 0 || d->pen->color().alpha() == 0))) {
771 delete oldNode;
772 return nullptr;
773 }
774
775 QSGInternalRectangleNode *rectangle = static_cast<QSGInternalRectangleNode *>(oldNode);
776 if (!rectangle) rectangle = d->sceneGraphContext()->createInternalRectangleNode();
777
778 rectangle->setRect(QRectF(0, 0, width(), height()));
779 rectangle->setColor(d->color);
780
781 if (d->pen && d->pen->isValid()) {
782 rectangle->setPenColor(d->pen->color());
783 qreal penWidth = d->pen->width();
784 if (d->pen->pixelAligned()) {
785 qreal dpr = d->effectiveDevicePixelRatio();
786 penWidth = qRound(d: penWidth * dpr) / dpr; // Ensures integer width after dpr scaling
787 }
788 rectangle->setPenWidth(penWidth);
789 rectangle->setAligned(false); // width rounding already done, so the Node should not do it
790 } else {
791 rectangle->setPenWidth(0);
792 }
793
794 rectangle->setRadius(d->radius);
795 if (d->extraRectangle.isAllocated()) {
796 const auto &extra = d->extraRectangle.value();
797 if (extra.isTopLeftRadiusSet)
798 rectangle->setTopLeftRadius(extra.topLeftRadius);
799 else
800 rectangle->resetTopLeftRadius();
801 if (extra.isTopRightRadiusSet)
802 rectangle->setTopRightRadius(extra.topRightRadius);
803 else
804 rectangle->resetTopRightRadius();
805 if (extra.isBottomLeftRadiusSet)
806 rectangle->setBottomLeftRadius(extra.bottomLeftRadius);
807 else
808 rectangle->resetBottomLeftRadius();
809 if (extra.isBottomRightRadiusSet)
810 rectangle->setBottomRightRadius(extra.bottomRightRadius);
811 else
812 rectangle->resetBottomRightRadius();
813 } else {
814 rectangle->resetTopLeftRadius();
815 rectangle->resetTopRightRadius();
816 rectangle->resetBottomLeftRadius();
817 rectangle->resetBottomRightRadius();
818 }
819 rectangle->setAntialiasing(antialiasing());
820
821 QGradientStops stops;
822 bool vertical = true;
823 if (d->gradient.isQObject()) {
824 auto gradient = qobject_cast<QQuickGradient*>(object: d->gradient.toQObject());
825 Q_ASSERT(gradient);
826 stops = gradient->gradientStops();
827 vertical = gradient->orientation() == QQuickGradient::Vertical;
828 } else if (d->gradient.isNumber() || d->gradient.isString()) {
829 QGradient preset(d->gradient.toVariant().value<QGradient::Preset>());
830 if (preset.type() == QGradient::LinearGradient) {
831 auto linearGradient = static_cast<QLinearGradient&>(preset);
832 const QPointF start = linearGradient.start();
833 const QPointF end = linearGradient.finalStop();
834 vertical = qAbs(t: start.y() - end.y()) >= qAbs(t: start.x() - end.x());
835 stops = linearGradient.stops();
836 if ((vertical && start.y() > end.y()) || (!vertical && start.x() > end.x())) {
837 // QSGInternalRectangleNode doesn't support stops in the wrong order,
838 // so we need to manually reverse them here.
839 QGradientStops reverseStops;
840 for (auto it = stops.rbegin(); it != stops.rend(); ++it) {
841 auto stop = *it;
842 stop.first = 1 - stop.first;
843 reverseStops.append(t: stop);
844 }
845 stops = reverseStops;
846 }
847 }
848 }
849 rectangle->setGradientStops(stops);
850 rectangle->setGradientVertical(vertical);
851
852 rectangle->update();
853
854 return rectangle;
855}
856
857QT_END_NAMESPACE
858
859#include "moc_qquickrectangle_p.cpp"
860

source code of qtdeclarative/src/quick/items/qquickrectangle.cpp