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 | |
18 | QT_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 | |
40 | QQuickPen::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 | |
49 | qreal QQuickPen::width() const |
50 | { |
51 | return m_width; |
52 | } |
53 | |
54 | void 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 | |
65 | QColor QQuickPen::color() const |
66 | { |
67 | return m_color; |
68 | } |
69 | |
70 | void 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 | |
78 | bool QQuickPen::pixelAligned() const |
79 | { |
80 | return m_aligned; |
81 | } |
82 | |
83 | void 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 | |
93 | bool 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 | */ |
119 | QQuickGradientStop::QQuickGradientStop(QObject *parent) |
120 | : QObject(parent) |
121 | { |
122 | } |
123 | |
124 | qreal QQuickGradientStop::position() const |
125 | { |
126 | return m_position; |
127 | } |
128 | |
129 | void QQuickGradientStop::setPosition(qreal position) |
130 | { |
131 | m_position = position; updateGradient(); |
132 | } |
133 | |
134 | QColor QQuickGradientStop::color() const |
135 | { |
136 | return m_color; |
137 | } |
138 | |
139 | void QQuickGradientStop::setColor(const QColor &color) |
140 | { |
141 | m_color = color; updateGradient(); |
142 | } |
143 | |
144 | void 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 | */ |
213 | QQuickGradient::QQuickGradient(QObject *parent) |
214 | : QObject(parent) |
215 | { |
216 | } |
217 | |
218 | QQuickGradient::~QQuickGradient() |
219 | { |
220 | } |
221 | |
222 | QQmlListProperty<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 | */ |
238 | void 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 | |
248 | QGradientStops 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 | |
260 | void QQuickGradient::doUpdate() |
261 | { |
262 | emit updated(); |
263 | } |
264 | |
265 | int QQuickRectanglePrivate::doUpdateSlotIdx = -1; |
266 | |
267 | void QQuickRectanglePrivate::maybeSetImplicitAntialiasing() |
268 | { |
269 | bool implicitAA = (radius != 0); |
270 | if (extraRectangle.isAllocated() && !implicitAA) { |
271 | implicitAA = extraRectangle.value().topLeftRadius > 0.0 |
272 | || extraRectangle.value().topRightRadius > 0.0 |
273 | || extraRectangle.value().bottomLeftRadius > 0.0 |
274 | || extraRectangle.value().bottomRightRadius > 0.0; |
275 | } |
276 | setImplicitAntialiasing(implicitAA); |
277 | } |
278 | /*! |
279 | \qmltype Rectangle |
280 | \nativetype QQuickRectangle |
281 | \inqmlmodule QtQuick |
282 | \inherits Item |
283 | \ingroup qtquick-visual |
284 | \brief Paints a filled rectangle with an optional border. |
285 | |
286 | Rectangle items are used to fill areas with solid color or gradients, and/or |
287 | to provide a rectangular border. |
288 | |
289 | \section1 Appearance |
290 | |
291 | Each Rectangle item is painted using either a solid fill color, specified using |
292 | the \l color property, or a gradient, defined using a Gradient type and set |
293 | using the \l gradient property. If both a color and a gradient are specified, |
294 | the gradient is used. |
295 | |
296 | You can add an optional border to a rectangle with its own color and thickness |
297 | by setting the \l border.color and \l border.width properties. Set the color |
298 | to "transparent" to paint a border without a fill color. |
299 | |
300 | You can also create rounded rectangles using the \l radius property. Since this |
301 | introduces curved edges to the corners of a rectangle, it may be appropriate to |
302 | set the \l Item::antialiasing property to improve its appearance. To set the |
303 | radii individually for different corners, you can use the properties |
304 | \l topLeftRadius, \l topRightRadius, \l bottomLeftRadius and |
305 | \l bottomRightRadius. |
306 | |
307 | \section1 Example Usage |
308 | |
309 | \div {class="float-right"} |
310 | \inlineimage declarative-rect.png |
311 | \enddiv |
312 | |
313 | The following example shows the effects of some of the common properties on a |
314 | Rectangle item, which in this case is used to create a square: |
315 | |
316 | \snippet qml/rectangle/rectangle.qml document |
317 | |
318 | \clearfloat |
319 | \section1 Performance |
320 | |
321 | Using the \l Item::antialiasing property improves the appearance of a rounded rectangle at |
322 | the cost of rendering performance. You should consider unsetting this property |
323 | for rectangles in motion, and only set it when they are stationary. |
324 | |
325 | \sa Image |
326 | */ |
327 | |
328 | QQuickRectangle::QQuickRectangle(QQuickItem *parent) |
329 | : QQuickItem(*(new QQuickRectanglePrivate), parent) |
330 | { |
331 | setFlag(flag: ItemHasContents); |
332 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |
333 | setAcceptTouchEvents(false); |
334 | #endif |
335 | } |
336 | |
337 | void QQuickRectangle::doUpdate() |
338 | { |
339 | update(); |
340 | } |
341 | |
342 | /*! |
343 | \qmlproperty bool QtQuick::Rectangle::antialiasing |
344 | |
345 | Used to decide if the Rectangle should use antialiasing or not. |
346 | \l {Antialiasing} provides information on the performance implications |
347 | of this property. |
348 | |
349 | The default is true for Rectangles with a radius, and false otherwise. |
350 | */ |
351 | |
352 | /*! |
353 | \qmlpropertygroup QtQuick::Rectangle::border |
354 | \qmlproperty int QtQuick::Rectangle::border.width |
355 | \qmlproperty color QtQuick::Rectangle::border.color |
356 | \qmlproperty bool QtQuick::Rectangle::border.pixelAligned |
357 | |
358 | The width and color used to draw the border of the rectangle. |
359 | |
360 | A width of 1 creates a thin line. For no line, use a width of 0 or a transparent color. |
361 | |
362 | \note The width of the rectangle's border does not affect the geometry of the |
363 | rectangle itself or its position relative to other items if anchors are used. |
364 | |
365 | The border is rendered within the rectangle's boundaries. |
366 | |
367 | If \c pixelAligned is \c true (the default), the rendered border width is rounded to a whole |
368 | number of pixels, after device pixel ratio scaling. Setting \c pixelAligned to \c false will |
369 | allow fractional border widths, which may be desirable when \c antialiasing is enabled. |
370 | */ |
371 | QQuickPen *QQuickRectangle::border() |
372 | { |
373 | Q_D(QQuickRectangle); |
374 | if (!d->pen) { |
375 | d->pen = new QQuickPen; |
376 | QQml_setParent_noEvent(object: d->pen, parent: this); |
377 | } |
378 | return d->pen; |
379 | } |
380 | |
381 | /*! |
382 | \qmlproperty var QtQuick::Rectangle::gradient |
383 | |
384 | The gradient to use to fill the rectangle. |
385 | |
386 | This property allows for the construction of simple vertical or horizontal gradients. |
387 | Other gradients may be formed by adding rotation to the rectangle. |
388 | |
389 | \div {class="float-left"} |
390 | \inlineimage declarative-rect_gradient.png |
391 | \enddiv |
392 | |
393 | \snippet qml/rectangle/rectangle-gradient.qml rectangles |
394 | \clearfloat |
395 | |
396 | The property also accepts gradient presets from QGradient::Preset. Note however |
397 | that due to Rectangle only supporting simple vertical or horizontal gradients, |
398 | any preset with an unsupported angle will revert to the closest representation. |
399 | |
400 | \snippet qml/rectangle/rectangle-gradient.qml presets |
401 | \clearfloat |
402 | |
403 | If both a gradient and a color are specified, the gradient will be used. |
404 | |
405 | \sa Gradient, color |
406 | */ |
407 | QJSValue QQuickRectangle::gradient() const |
408 | { |
409 | Q_D(const QQuickRectangle); |
410 | return d->gradient; |
411 | } |
412 | |
413 | void QQuickRectangle::setGradient(const QJSValue &gradient) |
414 | { |
415 | Q_D(QQuickRectangle); |
416 | if (d->gradient.equals(other: gradient)) |
417 | return; |
418 | |
419 | static int updatedSignalIdx = QMetaMethod::fromSignal(signal: &QQuickGradient::updated).methodIndex(); |
420 | if (d->doUpdateSlotIdx < 0) |
421 | d->doUpdateSlotIdx = QQuickRectangle::staticMetaObject.indexOfSlot(slot: "doUpdate()"); |
422 | |
423 | if (auto oldGradient = qobject_cast<QQuickGradient*>(object: d->gradient.toQObject())) |
424 | QMetaObject::disconnect(sender: oldGradient, signal_index: updatedSignalIdx, receiver: this, method_index: d->doUpdateSlotIdx); |
425 | |
426 | if (gradient.isQObject()) { |
427 | if (auto newGradient = qobject_cast<QQuickGradient*>(object: gradient.toQObject())) { |
428 | d->gradient = gradient; |
429 | QMetaObject::connect(sender: newGradient, signal_index: updatedSignalIdx, receiver: this, method_index: d->doUpdateSlotIdx); |
430 | } else { |
431 | qmlWarning(me: this) << "Can't assign " |
432 | << QQmlMetaType::prettyTypeName(object: gradient.toQObject()) << " to gradient property"; |
433 | d->gradient = QJSValue(); |
434 | } |
435 | } else if (gradient.isNumber() || gradient.isString()) { |
436 | static const QMetaEnum gradientPresetMetaEnum = QMetaEnum::fromType<QGradient::Preset>(); |
437 | Q_ASSERT(gradientPresetMetaEnum.isValid()); |
438 | |
439 | QGradient result; |
440 | |
441 | // This code could simply use gradient.toVariant().convert<QGradient::Preset>(), |
442 | // but QTBUG-76377 prevents us from doing error checks. So we need to |
443 | // do them manually. Also, NumPresets cannot be used. |
444 | |
445 | if (gradient.isNumber()) { |
446 | const auto preset = QGradient::Preset(gradient.toInt()); |
447 | if (preset != QGradient::NumPresets && gradientPresetMetaEnum.valueToKey(value: preset)) |
448 | result = QGradient(preset); |
449 | } else if (gradient.isString()) { |
450 | const auto presetName = gradient.toString(); |
451 | if (presetName != QLatin1String("NumPresets")) { |
452 | bool ok; |
453 | const auto presetInt = gradientPresetMetaEnum.keyToValue(qPrintable(presetName), ok: &ok); |
454 | if (ok) |
455 | result = QGradient(QGradient::Preset(presetInt)); |
456 | } |
457 | } |
458 | |
459 | if (result.type() != QGradient::NoGradient) { |
460 | d->gradient = gradient; |
461 | } else { |
462 | qmlWarning(me: this) << "No such gradient preset '"<< gradient.toString() << "'"; |
463 | d->gradient = QJSValue(); |
464 | } |
465 | } else if (gradient.isNull() || gradient.isUndefined()) { |
466 | d->gradient = gradient; |
467 | } else { |
468 | qmlWarning(me: this) << "Unknown gradient type. Expected int, string, or Gradient"; |
469 | d->gradient = QJSValue(); |
470 | } |
471 | |
472 | update(); |
473 | } |
474 | |
475 | void QQuickRectangle::resetGradient() |
476 | { |
477 | setGradient(QJSValue()); |
478 | } |
479 | |
480 | /*! |
481 | \qmlproperty real QtQuick::Rectangle::radius |
482 | This property holds the corner radius used to draw a rounded rectangle. |
483 | |
484 | If radius is non-zero, the rectangle will be painted as a rounded rectangle, |
485 | otherwise it will be painted as a normal rectangle. Individual corner radii |
486 | can be set as well (see below). These values will override \l radius. If |
487 | they are unset (by setting them to \c undefined), \l radius will be used instead. |
488 | |
489 | \sa topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius |
490 | */ |
491 | qreal QQuickRectangle::radius() const |
492 | { |
493 | Q_D(const QQuickRectangle); |
494 | return d->radius; |
495 | } |
496 | |
497 | void QQuickRectangle::setRadius(qreal radius) |
498 | { |
499 | Q_D(QQuickRectangle); |
500 | if (d->radius == radius) |
501 | return; |
502 | |
503 | d->radius = radius; |
504 | d->maybeSetImplicitAntialiasing(); |
505 | |
506 | update(); |
507 | emit radiusChanged(); |
508 | |
509 | if (d->extraRectangle.isAllocated()) { |
510 | if (d->extraRectangle->topLeftRadius < 0.) |
511 | emit topLeftRadiusChanged(); |
512 | if (d->extraRectangle->topRightRadius < 0.) |
513 | emit topRightRadiusChanged(); |
514 | if (d->extraRectangle->bottomLeftRadius < 0.) |
515 | emit bottomLeftRadiusChanged(); |
516 | if (d->extraRectangle->bottomRightRadius < 0.) |
517 | emit bottomRightRadiusChanged(); |
518 | } else { |
519 | emit topLeftRadiusChanged(); |
520 | emit topRightRadiusChanged(); |
521 | emit bottomLeftRadiusChanged(); |
522 | emit bottomRightRadiusChanged(); |
523 | } |
524 | } |
525 | |
526 | /*! |
527 | \since 6.7 |
528 | \qmlproperty real QtQuick::Rectangle::topLeftRadius |
529 | This property holds the radius used to draw the top left corner. |
530 | |
531 | If \l topLeftRadius is not set, \l radius will be used instead. |
532 | If \l topLeftRadius is zero, the corner will be sharp. |
533 | |
534 | \note This API is considered tech preview and may change or be removed in |
535 | future versions of Qt. |
536 | |
537 | \sa radius, topRightRadius, bottomLeftRadius, bottomRightRadius |
538 | */ |
539 | qreal QQuickRectangle::topLeftRadius() const |
540 | { |
541 | Q_D(const QQuickRectangle); |
542 | if (d->extraRectangle.isAllocated() && d->extraRectangle->topLeftRadius >= 0.) |
543 | return d->extraRectangle.value().topLeftRadius; |
544 | return d->radius; |
545 | } |
546 | |
547 | void QQuickRectangle::setTopLeftRadius(qreal radius) |
548 | { |
549 | Q_D(QQuickRectangle); |
550 | if (d->extraRectangle.value().topLeftRadius == radius) |
551 | return; |
552 | |
553 | if (radius < 0) { // use the fact that radius < 0 resets the radius. |
554 | qmlWarning(me: this) << "topLeftRadius ("<< radius << ") cannot be less than 0."; |
555 | return; |
556 | } |
557 | d->extraRectangle.value().topLeftRadius = radius; |
558 | d->maybeSetImplicitAntialiasing(); |
559 | |
560 | update(); |
561 | emit topLeftRadiusChanged(); |
562 | } |
563 | |
564 | void QQuickRectangle::resetTopLeftRadius() |
565 | { |
566 | Q_D(QQuickRectangle); |
567 | if (!d->extraRectangle.isAllocated()) |
568 | return; |
569 | if (d->extraRectangle.value().topLeftRadius < 0) |
570 | return; |
571 | |
572 | d->extraRectangle.value().topLeftRadius = -1.; |
573 | d->maybeSetImplicitAntialiasing(); |
574 | |
575 | update(); |
576 | emit topLeftRadiusChanged(); |
577 | } |
578 | |
579 | /*! |
580 | \since 6.7 |
581 | \qmlproperty real QtQuick::Rectangle::topRightRadius |
582 | This property holds the radius used to draw the top right corner. |
583 | |
584 | If \l topRightRadius is not set, \l radius will be used instead. |
585 | If \l topRightRadius is zero, the corner will be sharp. |
586 | |
587 | \note This API is considered tech preview and may change or be removed in |
588 | future versions of Qt. |
589 | |
590 | \sa radius, topLeftRadius, bottomLeftRadius, bottomRightRadius |
591 | */ |
592 | qreal QQuickRectangle::topRightRadius() const |
593 | { |
594 | Q_D(const QQuickRectangle); |
595 | if (d->extraRectangle.isAllocated() && d->extraRectangle->topRightRadius >= 0.) |
596 | return d->extraRectangle.value().topRightRadius; |
597 | return d->radius; |
598 | } |
599 | |
600 | void QQuickRectangle::setTopRightRadius(qreal radius) |
601 | { |
602 | Q_D(QQuickRectangle); |
603 | if (d->extraRectangle.value().topRightRadius == radius) |
604 | return; |
605 | |
606 | if (radius < 0) { // use the fact that radius < 0 resets the radius. |
607 | qmlWarning(me: this) << "topRightRadius ("<< radius << ") cannot be less than 0."; |
608 | return; |
609 | } |
610 | d->extraRectangle.value().topRightRadius = radius; |
611 | d->maybeSetImplicitAntialiasing(); |
612 | |
613 | update(); |
614 | emit topRightRadiusChanged(); |
615 | } |
616 | |
617 | void QQuickRectangle::resetTopRightRadius() |
618 | { |
619 | Q_D(QQuickRectangle); |
620 | if (!d->extraRectangle.isAllocated()) |
621 | return; |
622 | if (d->extraRectangle.value().topRightRadius < 0) |
623 | return; |
624 | |
625 | d->extraRectangle.value().topRightRadius = -1.; |
626 | d->maybeSetImplicitAntialiasing(); |
627 | |
628 | update(); |
629 | emit topRightRadiusChanged(); |
630 | } |
631 | |
632 | /*! |
633 | \since 6.7 |
634 | \qmlproperty real QtQuick::Rectangle::bottomLeftRadius |
635 | This property holds the radius used to draw the bottom left corner. |
636 | |
637 | If \l bottomLeftRadius is not set, \l radius will be used instead. |
638 | If \l bottomLeftRadius is zero, the corner will be sharp. |
639 | |
640 | \note This API is considered tech preview and may change or be removed in |
641 | future versions of Qt. |
642 | |
643 | \sa radius, topLeftRadius, topRightRadius, bottomRightRadius |
644 | */ |
645 | qreal QQuickRectangle::bottomLeftRadius() const |
646 | { |
647 | Q_D(const QQuickRectangle); |
648 | if (d->extraRectangle.isAllocated() && d->extraRectangle->bottomLeftRadius >= 0.) |
649 | return d->extraRectangle.value().bottomLeftRadius; |
650 | return d->radius; |
651 | } |
652 | |
653 | void QQuickRectangle::setBottomLeftRadius(qreal radius) |
654 | { |
655 | Q_D(QQuickRectangle); |
656 | if (d->extraRectangle.value().bottomLeftRadius == radius) |
657 | return; |
658 | |
659 | if (radius < 0) { // use the fact that radius < 0 resets the radius. |
660 | qmlWarning(me: this) << "bottomLeftRadius ("<< radius << ") cannot be less than 0."; |
661 | return; |
662 | } |
663 | |
664 | d->extraRectangle.value().bottomLeftRadius = radius; |
665 | d->maybeSetImplicitAntialiasing(); |
666 | |
667 | update(); |
668 | emit bottomLeftRadiusChanged(); |
669 | } |
670 | |
671 | void QQuickRectangle::resetBottomLeftRadius() |
672 | { |
673 | Q_D(QQuickRectangle); |
674 | if (!d->extraRectangle.isAllocated()) |
675 | return; |
676 | if (d->extraRectangle.value().bottomLeftRadius < 0) |
677 | return; |
678 | |
679 | d->extraRectangle.value().bottomLeftRadius = -1.; |
680 | d->maybeSetImplicitAntialiasing(); |
681 | |
682 | update(); |
683 | emit bottomLeftRadiusChanged(); |
684 | } |
685 | |
686 | /*! |
687 | \since 6.7 |
688 | \qmlproperty real QtQuick::Rectangle::bottomRightRadius |
689 | This property holds the radius used to draw the bottom right corner. |
690 | |
691 | If \l bottomRightRadius is not set, \l radius will be used instead. |
692 | If \l bottomRightRadius is zero, the corner will be sharp. |
693 | |
694 | \note This API is considered tech preview and may change or be removed in |
695 | future versions of Qt. |
696 | |
697 | \sa radius, topLeftRadius, topRightRadius, bottomLeftRadius |
698 | */ |
699 | qreal QQuickRectangle::bottomRightRadius() const |
700 | { |
701 | Q_D(const QQuickRectangle); |
702 | if (d->extraRectangle.isAllocated() && d->extraRectangle->bottomRightRadius >= 0.) |
703 | return d->extraRectangle.value().bottomRightRadius; |
704 | return d->radius; |
705 | } |
706 | |
707 | void QQuickRectangle::setBottomRightRadius(qreal radius) |
708 | { |
709 | Q_D(QQuickRectangle); |
710 | if (d->extraRectangle.value().bottomRightRadius == radius) |
711 | return; |
712 | |
713 | if (radius < 0) { // use the fact that radius < 0 resets the radius. |
714 | qmlWarning(me: this) << "bottomRightRadius ("<< radius << ") cannot be less than 0."; |
715 | return; |
716 | } |
717 | |
718 | d->extraRectangle.value().bottomRightRadius = radius; |
719 | d->maybeSetImplicitAntialiasing(); |
720 | |
721 | update(); |
722 | emit bottomRightRadiusChanged(); |
723 | } |
724 | |
725 | void QQuickRectangle::resetBottomRightRadius() |
726 | { |
727 | Q_D(QQuickRectangle); |
728 | if (!d->extraRectangle.isAllocated()) |
729 | return; |
730 | if (d->extraRectangle.value().bottomRightRadius < 0) |
731 | return; |
732 | |
733 | d->extraRectangle.value().bottomRightRadius = -1.; |
734 | d->maybeSetImplicitAntialiasing(); |
735 | |
736 | update(); |
737 | emit bottomRightRadiusChanged(); |
738 | } |
739 | |
740 | /*! |
741 | \qmlproperty color QtQuick::Rectangle::color |
742 | This property holds the color used to fill the rectangle. |
743 | |
744 | The default color is white. |
745 | |
746 | \div {class="float-right"} |
747 | \inlineimage rect-color.png |
748 | \enddiv |
749 | |
750 | The following example shows rectangles with colors specified |
751 | using hexadecimal and named color notation: |
752 | |
753 | \snippet qml/rectangle/rectangle-colors.qml rectangles |
754 | |
755 | \clearfloat |
756 | If both a gradient and a color are specified, the gradient will be used. |
757 | |
758 | \sa gradient |
759 | */ |
760 | QColor QQuickRectangle::color() const |
761 | { |
762 | Q_D(const QQuickRectangle); |
763 | return d->color; |
764 | } |
765 | |
766 | void QQuickRectangle::setColor(const QColor &c) |
767 | { |
768 | Q_D(QQuickRectangle); |
769 | if (d->color == c) |
770 | return; |
771 | |
772 | d->color = c; |
773 | update(); |
774 | emit colorChanged(); |
775 | } |
776 | |
777 | QSGNode *QQuickRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) |
778 | { |
779 | Q_UNUSED(data); |
780 | Q_D(QQuickRectangle); |
781 | |
782 | if (width() <= 0 || height() <= 0 |
783 | || (d->color.alpha() == 0 && (!d->pen || d->pen->width() == 0 || d->pen->color().alpha() == 0))) { |
784 | delete oldNode; |
785 | return nullptr; |
786 | } |
787 | |
788 | QSGInternalRectangleNode *rectangle = static_cast<QSGInternalRectangleNode *>(oldNode); |
789 | if (!rectangle) rectangle = d->sceneGraphContext()->createInternalRectangleNode(); |
790 | |
791 | rectangle->setRect(QRectF(0, 0, width(), height())); |
792 | rectangle->setColor(d->color); |
793 | |
794 | if (d->pen && d->pen->isValid()) { |
795 | rectangle->setPenColor(d->pen->color()); |
796 | qreal penWidth = d->pen->width(); |
797 | if (d->pen->pixelAligned()) { |
798 | qreal dpr = window() ? window()->effectiveDevicePixelRatio() : 1.0; |
799 | penWidth = qRound(d: penWidth * dpr) / dpr; // Ensures integer width after dpr scaling |
800 | } |
801 | rectangle->setPenWidth(penWidth); |
802 | rectangle->setAligned(false); // width rounding already done, so the Node should not do it |
803 | } else { |
804 | rectangle->setPenWidth(0); |
805 | } |
806 | |
807 | rectangle->setRadius(d->radius); |
808 | if (d->extraRectangle.isAllocated()) { |
809 | rectangle->setTopLeftRadius(d->extraRectangle.value().topLeftRadius); |
810 | rectangle->setTopRightRadius(d->extraRectangle.value().topRightRadius); |
811 | rectangle->setBottomLeftRadius(d->extraRectangle.value().bottomLeftRadius); |
812 | rectangle->setBottomRightRadius(d->extraRectangle.value().bottomRightRadius); |
813 | } else { |
814 | rectangle->setTopLeftRadius(-1.); |
815 | rectangle->setTopRightRadius(-1.); |
816 | rectangle->setBottomLeftRadius(-1.); |
817 | rectangle->setBottomRightRadius(-1.); |
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 | |
857 | QT_END_NAMESPACE |
858 | |
859 | #include "moc_qquickrectangle_p.cpp" |
860 |
Definitions
- QQuickPen
- width
- setWidth
- color
- setColor
- pixelAligned
- setPixelAligned
- isValid
- QQuickGradientStop
- position
- setPosition
- color
- setColor
- updateGradient
- QQuickGradient
- ~QQuickGradient
- stops
- setOrientation
- gradientStops
- doUpdate
- doUpdateSlotIdx
- maybeSetImplicitAntialiasing
- QQuickRectangle
- doUpdate
- border
- gradient
- setGradient
- resetGradient
- radius
- setRadius
- topLeftRadius
- setTopLeftRadius
- resetTopLeftRadius
- topRightRadius
- setTopRightRadius
- resetTopRightRadius
- bottomLeftRadius
- setBottomLeftRadius
- resetBottomLeftRadius
- bottomRightRadius
- setBottomRightRadius
- resetBottomRightRadius
- color
- setColor
Learn Advanced QML with KDAB
Find out more