1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquickrectangle_p.h"
41#include "qquickrectangle_p_p.h"
42
43#include <QtQml/qqmlinfo.h>
44
45#include <QtQuick/private/qsgcontext_p.h>
46#include <private/qsgadaptationlayer_p.h>
47
48#include <private/qqmlmetatype_p.h>
49
50#include <QtGui/qpixmapcache.h>
51#include <QtCore/qmath.h>
52#include <QtCore/qmetaobject.h>
53
54QT_BEGIN_NAMESPACE
55
56// XXX todo - should we change rectangle to draw entirely within its width/height?
57/*!
58 \internal
59 \class QQuickPen
60 \brief For specifying a pen used for drawing rectangle borders on a QQuickView
61
62 By default, the pen is invalid and nothing is drawn. You must either set a color (then the default
63 width is 1) or a width (then the default color is black).
64
65 A width of 1 indicates is a single-pixel line on the border of the item being painted.
66
67 Example:
68 \qml
69 Rectangle {
70 border.width: 2
71 border.color: "red"
72 }
73 \endqml
74*/
75
76QQuickPen::QQuickPen(QObject *parent)
77 : QObject(parent)
78 , m_width(1)
79 , m_color(Qt::black)
80 , m_aligned(true)
81 , m_valid(false)
82{
83}
84
85qreal QQuickPen::width() const
86{
87 return m_width;
88}
89
90void QQuickPen::setWidth(qreal w)
91{
92 if (m_width == w && m_valid)
93 return;
94
95 m_width = w;
96 m_valid = m_color.alpha() && (qRound(d: m_width) >= 1 || (!m_aligned && m_width > 0));
97 static_cast<QQuickItem*>(parent())->update();
98 emit penChanged();
99}
100
101QColor QQuickPen::color() const
102{
103 return m_color;
104}
105
106void QQuickPen::setColor(const QColor &c)
107{
108 m_color = c;
109 m_valid = m_color.alpha() && (qRound(d: m_width) >= 1 || (!m_aligned && m_width > 0));
110 static_cast<QQuickItem*>(parent())->update();
111 emit penChanged();
112}
113
114bool QQuickPen::pixelAligned() const
115{
116 return m_aligned;
117}
118
119void QQuickPen::setPixelAligned(bool aligned)
120{
121 if (aligned == m_aligned)
122 return;
123 m_aligned = aligned;
124 m_valid = m_color.alpha() && (qRound(d: m_width) >= 1 || (!m_aligned && m_width > 0));
125 static_cast<QQuickItem*>(parent())->update();
126 emit penChanged();
127}
128
129bool QQuickPen::isValid() const
130{
131 return m_valid;
132}
133
134/*!
135 \qmltype GradientStop
136 \instantiates QQuickGradientStop
137 \inqmlmodule QtQuick
138 \ingroup qtquick-visual-utility
139 \brief Defines the color at a position in a Gradient.
140
141 \sa Gradient
142*/
143
144/*!
145 \qmlproperty real QtQuick::GradientStop::position
146 \qmlproperty color QtQuick::GradientStop::color
147
148 The position and color properties describe the color used at a given
149 position in a gradient, as represented by a gradient stop.
150
151 The default position is 0.0; the default color is black.
152
153 \sa Gradient
154*/
155QQuickGradientStop::QQuickGradientStop(QObject *parent)
156 : QObject(parent)
157{
158}
159
160qreal QQuickGradientStop::position() const
161{
162 return m_position;
163}
164
165void QQuickGradientStop::setPosition(qreal position)
166{
167 m_position = position; updateGradient();
168}
169
170QColor QQuickGradientStop::color() const
171{
172 return m_color;
173}
174
175void QQuickGradientStop::setColor(const QColor &color)
176{
177 m_color = color; updateGradient();
178}
179
180void QQuickGradientStop::updateGradient()
181{
182 if (QQuickGradient *grad = qobject_cast<QQuickGradient*>(object: parent()))
183 grad->doUpdate();
184}
185
186/*!
187 \qmltype Gradient
188 \instantiates QQuickGradient
189 \inqmlmodule QtQuick
190 \ingroup qtquick-visual-utility
191 \brief Defines a gradient fill.
192
193 A gradient is defined by two or more colors, which will be blended seamlessly.
194
195 The colors are specified as a set of GradientStop child items, each of
196 which defines a position on the gradient from 0.0 to 1.0 and a color.
197 The position of each GradientStop is defined by setting its
198 \l{GradientStop::}{position} property; its color is defined using its
199 \l{GradientStop::}{color} property.
200
201 A gradient without any gradient stops is rendered as a solid white fill.
202
203 Note that this item is not a visual representation of a gradient. To display a
204 gradient, use a visual item (like \l Rectangle) which supports the use
205 of gradients.
206
207 \section1 Example Usage
208
209 \div {class="float-right"}
210 \inlineimage qml-gradient.png
211 \enddiv
212
213 The following example declares a \l Rectangle item with a gradient starting
214 with red, blending to yellow at one third of the height of the rectangle,
215 and ending with green:
216
217 \snippet qml/gradient.qml code
218
219 \clearfloat
220 \section1 Performance and Limitations
221
222 Calculating gradients can be computationally expensive compared to the use
223 of solid color fills or images. Consider using gradients for static items
224 in a user interface.
225
226 Since Qt 5.12, vertical and horizontal linear gradients can be applied to items.
227 If you need to apply angled gradients, a combination of rotation and clipping
228 can be applied to the relevant items. Alternatively, consider using
229 QtQuick.Shapes::LinearGradient or QtGraphicalEffects::LinearGradient. These
230 approaches can all introduce additional performance requirements for your application.
231
232 The use of animations involving gradient stops may not give the desired
233 result. An alternative way to animate gradients is to use pre-generated
234 images or SVG drawings containing gradients.
235
236 \sa GradientStop
237*/
238
239/*!
240 \qmlproperty list<GradientStop> QtQuick::Gradient::stops
241 \default
242
243 This property holds the gradient stops describing the gradient.
244
245 By default, this property contains an empty list.
246
247 To set the gradient stops, define them as children of the Gradient.
248*/
249QQuickGradient::QQuickGradient(QObject *parent)
250: QObject(parent)
251{
252}
253
254QQuickGradient::~QQuickGradient()
255{
256}
257
258QQmlListProperty<QQuickGradientStop> QQuickGradient::stops()
259{
260 return QQmlListProperty<QQuickGradientStop>(this, &m_stops);
261}
262
263/*!
264 \qmlproperty enumeration QtQuick::Gradient::orientation
265 \since 5.12
266
267 Set this property to define the direction of the gradient.
268 \list
269 \li Gradient.Vertical - a vertical gradient
270 \li Gradient.Horizontal - a horizontal gradient
271 \endlist
272
273 The default is Gradient.Vertical.
274*/
275void QQuickGradient::setOrientation(Orientation orientation)
276{
277 if (m_orientation == orientation)
278 return;
279
280 m_orientation = orientation;
281 emit orientationChanged();
282 emit updated();
283}
284
285QGradientStops QQuickGradient::gradientStops() const
286{
287 QGradientStops stops;
288 for (int i = 0; i < m_stops.size(); ++i){
289 int j = 0;
290 while (j < stops.size() && stops.at(i: j).first < m_stops[i]->position())
291 j++;
292 stops.insert(i: j, t: QGradientStop(m_stops.at(i)->position(), m_stops.at(i)->color()));
293 }
294 return stops;
295}
296
297void QQuickGradient::doUpdate()
298{
299 emit updated();
300}
301
302int QQuickRectanglePrivate::doUpdateSlotIdx = -1;
303
304/*!
305 \qmltype Rectangle
306 \instantiates QQuickRectangle
307 \inqmlmodule QtQuick
308 \inherits Item
309 \ingroup qtquick-visual
310 \brief Paints a filled rectangle with an optional border.
311
312 Rectangle items are used to fill areas with solid color or gradients, and/or
313 to provide a rectangular border.
314
315 \section1 Appearance
316
317 Each Rectangle item is painted using either a solid fill color, specified using
318 the \l color property, or a gradient, defined using a Gradient type and set
319 using the \l gradient property. If both a color and a gradient are specified,
320 the gradient is used.
321
322 You can add an optional border to a rectangle with its own color and thickness
323 by setting the \l border.color and \l border.width properties. Set the color
324 to "transparent" to paint a border without a fill color.
325
326 You can also create rounded rectangles using the \l radius property. Since this
327 introduces curved edges to the corners of a rectangle, it may be appropriate to
328 set the \l Item::antialiasing property to improve its appearance.
329
330 \section1 Example Usage
331
332 \div {class="float-right"}
333 \inlineimage declarative-rect.png
334 \enddiv
335
336 The following example shows the effects of some of the common properties on a
337 Rectangle item, which in this case is used to create a square:
338
339 \snippet qml/rectangle/rectangle.qml document
340
341 \clearfloat
342 \section1 Performance
343
344 Using the \l Item::antialiasing property improves the appearance of a rounded rectangle at
345 the cost of rendering performance. You should consider unsetting this property
346 for rectangles in motion, and only set it when they are stationary.
347
348 \sa Image
349*/
350
351QQuickRectangle::QQuickRectangle(QQuickItem *parent)
352: QQuickItem(*(new QQuickRectanglePrivate), parent)
353{
354 setFlag(flag: ItemHasContents);
355#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
356 setAcceptTouchEvents(false);
357#endif
358}
359
360void QQuickRectangle::doUpdate()
361{
362 update();
363}
364
365/*!
366 \qmlproperty bool QtQuick::Rectangle::antialiasing
367
368 Used to decide if the Rectangle should use antialiasing or not.
369 \l {Antialiasing} provides information on the performance implications
370 of this property.
371
372 The default is true for Rectangles with a radius, and false otherwise.
373*/
374
375/*!
376 \qmlpropertygroup QtQuick::Rectangle::border
377 \qmlproperty int QtQuick::Rectangle::border.width
378 \qmlproperty color QtQuick::Rectangle::border.color
379
380 The width and color used to draw the border of the rectangle.
381
382 A width of 1 creates a thin line. For no line, use a width of 0 or a transparent color.
383
384 \note The width of the rectangle's border does not affect the geometry of the
385 rectangle itself or its position relative to other items if anchors are used.
386
387 The border is rendered within the rectangle's boundaries.
388*/
389QQuickPen *QQuickRectangle::border()
390{
391 Q_D(QQuickRectangle);
392 if (!d->pen) {
393 d->pen = new QQuickPen;
394 QQml_setParent_noEvent(object: d->pen, parent: this);
395 }
396 return d->pen;
397}
398
399/*!
400 \qmlproperty any QtQuick::Rectangle::gradient
401
402 The gradient to use to fill the rectangle.
403
404 This property allows for the construction of simple vertical or horizontal gradients.
405 Other gradients may be formed by adding rotation to the rectangle.
406
407 \div {class="float-left"}
408 \inlineimage declarative-rect_gradient.png
409 \enddiv
410
411 \snippet qml/rectangle/rectangle-gradient.qml rectangles
412 \clearfloat
413
414 The property also accepts gradient presets from QGradient::Preset. Note however
415 that due to Rectangle only supporting simple vertical or horizontal gradients,
416 any preset with an unsupported angle will revert to the closest representation.
417
418 \snippet qml/rectangle/rectangle-gradient.qml presets
419 \clearfloat
420
421 If both a gradient and a color are specified, the gradient will be used.
422
423 \sa Gradient, color
424*/
425QJSValue QQuickRectangle::gradient() const
426{
427 Q_D(const QQuickRectangle);
428 return d->gradient;
429}
430
431void QQuickRectangle::setGradient(const QJSValue &gradient)
432{
433 Q_D(QQuickRectangle);
434 if (d->gradient.equals(other: gradient))
435 return;
436
437 static int updatedSignalIdx = QMetaMethod::fromSignal(signal: &QQuickGradient::updated).methodIndex();
438 if (d->doUpdateSlotIdx < 0)
439 d->doUpdateSlotIdx = QQuickRectangle::staticMetaObject.indexOfSlot(slot: "doUpdate()");
440
441 if (auto oldGradient = qobject_cast<QQuickGradient*>(object: d->gradient.toQObject()))
442 QMetaObject::disconnect(sender: oldGradient, signal_index: updatedSignalIdx, receiver: this, method_index: d->doUpdateSlotIdx);
443
444 if (gradient.isQObject()) {
445 if (auto newGradient = qobject_cast<QQuickGradient*>(object: gradient.toQObject())) {
446 d->gradient = gradient;
447 QMetaObject::connect(sender: newGradient, signal_index: updatedSignalIdx, receiver: this, method_index: d->doUpdateSlotIdx);
448 } else {
449 qmlWarning(me: this) << "Can't assign "
450 << QQmlMetaType::prettyTypeName(object: gradient.toQObject()) << " to gradient property";
451 d->gradient = QJSValue();
452 }
453 } else if (gradient.isNumber() || gradient.isString()) {
454 static const QMetaEnum gradientPresetMetaEnum = QMetaEnum::fromType<QGradient::Preset>();
455 Q_ASSERT(gradientPresetMetaEnum.isValid());
456
457 QGradient result;
458
459 // This code could simply use gradient.toVariant().convert<QGradient::Preset>(),
460 // but QTBUG-76377 prevents us from doing error checks. So we need to
461 // do them manually. Also, NumPresets cannot be used.
462
463 if (gradient.isNumber()) {
464 const auto preset = QGradient::Preset(gradient.toInt());
465 if (preset != QGradient::NumPresets && gradientPresetMetaEnum.valueToKey(value: preset))
466 result = QGradient(preset);
467 } else if (gradient.isString()) {
468 const auto presetName = gradient.toString();
469 if (presetName != QLatin1String("NumPresets")) {
470 bool ok;
471 const auto presetInt = gradientPresetMetaEnum.keyToValue(qPrintable(presetName), ok: &ok);
472 if (ok)
473 result = QGradient(QGradient::Preset(presetInt));
474 }
475 }
476
477 if (result.type() != QGradient::NoGradient) {
478 d->gradient = gradient;
479 } else {
480 qmlWarning(me: this) << "No such gradient preset '" << gradient.toString() << "'";
481 d->gradient = QJSValue();
482 }
483 } else if (gradient.isNull() || gradient.isUndefined()) {
484 d->gradient = gradient;
485 } else {
486 qmlWarning(me: this) << "Unknown gradient type. Expected int, string, or Gradient";
487 d->gradient = QJSValue();
488 }
489
490 update();
491}
492
493void QQuickRectangle::resetGradient()
494{
495 setGradient(QJSValue());
496}
497
498/*!
499 \qmlproperty real QtQuick::Rectangle::radius
500 This property holds the corner radius used to draw a rounded rectangle.
501
502 If radius is non-zero, the rectangle will be painted as a rounded rectangle, otherwise it will be
503 painted as a normal rectangle. The same radius is used by all 4 corners; there is currently
504 no way to specify different radii for different corners.
505*/
506qreal QQuickRectangle::radius() const
507{
508 Q_D(const QQuickRectangle);
509 return d->radius;
510}
511
512void QQuickRectangle::setRadius(qreal radius)
513{
514 Q_D(QQuickRectangle);
515 if (d->radius == radius)
516 return;
517
518 d->radius = radius;
519 d->setImplicitAntialiasing(radius != 0.0);
520
521 update();
522 emit radiusChanged();
523}
524
525/*!
526 \qmlproperty color QtQuick::Rectangle::color
527 This property holds the color used to fill the rectangle.
528
529 The default color is white.
530
531 \div {class="float-right"}
532 \inlineimage rect-color.png
533 \enddiv
534
535 The following example shows rectangles with colors specified
536 using hexadecimal and named color notation:
537
538 \snippet qml/rectangle/rectangle-colors.qml rectangles
539
540 \clearfloat
541 If both a gradient and a color are specified, the gradient will be used.
542
543 \sa gradient
544*/
545QColor QQuickRectangle::color() const
546{
547 Q_D(const QQuickRectangle);
548 return d->color;
549}
550
551void QQuickRectangle::setColor(const QColor &c)
552{
553 Q_D(QQuickRectangle);
554 if (d->color == c)
555 return;
556
557 d->color = c;
558 update();
559 emit colorChanged();
560}
561
562QSGNode *QQuickRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
563{
564 Q_UNUSED(data);
565 Q_D(QQuickRectangle);
566
567 if (width() <= 0 || height() <= 0
568 || (d->color.alpha() == 0 && (!d->pen || d->pen->width() == 0 || d->pen->color().alpha() == 0))) {
569 delete oldNode;
570 return nullptr;
571 }
572
573 QSGInternalRectangleNode *rectangle = static_cast<QSGInternalRectangleNode *>(oldNode);
574 if (!rectangle) rectangle = d->sceneGraphContext()->createInternalRectangleNode();
575
576 rectangle->setRect(QRectF(0, 0, width(), height()));
577 rectangle->setColor(d->color);
578
579 if (d->pen && d->pen->isValid()) {
580 rectangle->setPenColor(d->pen->color());
581 rectangle->setPenWidth(d->pen->width());
582 rectangle->setAligned(d->pen->pixelAligned());
583 } else {
584 rectangle->setPenWidth(0);
585 }
586
587 rectangle->setRadius(d->radius);
588 rectangle->setAntialiasing(antialiasing());
589
590 QGradientStops stops;
591 bool vertical = true;
592 if (d->gradient.isQObject()) {
593 auto gradient = qobject_cast<QQuickGradient*>(object: d->gradient.toQObject());
594 Q_ASSERT(gradient);
595 stops = gradient->gradientStops();
596 vertical = gradient->orientation() == QQuickGradient::Vertical;
597 } else if (d->gradient.isNumber() || d->gradient.isString()) {
598 QGradient preset(d->gradient.toVariant().value<QGradient::Preset>());
599 if (preset.type() == QGradient::LinearGradient) {
600 auto linearGradient = static_cast<QLinearGradient&>(preset);
601 const QPointF start = linearGradient.start();
602 const QPointF end = linearGradient.finalStop();
603 vertical = qAbs(t: start.y() - end.y()) >= qAbs(t: start.x() - end.x());
604 stops = linearGradient.stops();
605 if ((vertical && start.y() > end.y()) || (!vertical && start.x() > end.x())) {
606 // QSGInternalRectangleNode doesn't support stops in the wrong order,
607 // so we need to manually reverse them here.
608 QGradientStops reverseStops;
609 for (auto it = stops.crbegin(); it != stops.crend(); ++it) {
610 auto stop = *it;
611 stop.first = 1 - stop.first;
612 reverseStops.append(t: stop);
613 }
614 stops = reverseStops;
615 }
616 }
617 }
618 rectangle->setGradientStops(stops);
619 rectangle->setGradientVertical(vertical);
620
621 rectangle->update();
622
623 return rectangle;
624}
625
626QT_END_NAMESPACE
627
628#include "moc_qquickrectangle_p.cpp"
629

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