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
qquickshape.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
10#include <private/qsgcurvestrokenode_p.h>
11#include <private/qsgplaintexture_p.h>
12#include <QtGui/private/qdrawhelper_p.h>
13#include <QOpenGLFunctions>
14#include <QLoggingCategory>
15#include <rhi/qrhi.h>
16
17static void initResources()
18{
19#if defined(QT_STATIC)
20 Q_INIT_RESOURCE(qtquickshapes_shaders);
21#endif
22}
23
25
26Q_STATIC_LOGGING_CATEGORY(QQSHAPE_LOG_TIME_DIRTY_SYNC, "qt.shape.time.sync")
27Q_STATIC_LOGGING_CATEGORY(lcShapeSync, "qt.quick.shapes.shape.sync")
28
29/*!
30 \keyword Qt Quick Shapes
31 \qmlmodule QtQuick.Shapes 1.\QtMinorVersion
32 \title Qt Quick Shapes QML Types
33 \ingroup qmlmodules
34 \brief Provides QML types for drawing stroked and filled shapes.
35
36 To use the types in this module, import the module with the following line:
37
38 \qml
39 import QtQuick.Shapes
40 \endqml
41
42 Qt Quick Shapes provides tools for drawing arbitrary shapes in a Qt Quick scene.
43 \l{Shape}{Shapes} can be constructed from basic building blocks like \l{PathLine}{lines} and
44 \l{PathCubic}{curves} that define sub-shapes. The sub-shapes can then be filled with solid
45 colors or gradients, and an outline stroke can be defined.
46
47 Qt Quick Shapes also supports higher level path element types, such as \l{PathText}{text} and
48 \l{PathSvg}{SVG path descriptions}. The currently supported element types is: PathMove,
49 PathLine, PathQuad, PathCubic, PathArc, PathText and PathSvg.
50
51 Qt Quick Shapes triangulates the shapes and renders the corresponding triangles on the GPU.
52 Therefore, altering the control points of elements will lead to re-triangulation of the
53 affected paths, at some performance cost. In addition, curves are flattened before they are
54 rendered, so applying a very high scale to the shape may show artifacts where it is visible
55 that the curves are represented by a sequence of smaller, straight lines.
56
57 \note By default, Qt Quick Shapes relies on multi-sampling for anti-aliasing. This can be
58 enabled for the entire application or window using the corresponding settings in QSurfaceFormat.
59 It can also be enabled for only the shape, by setting its \l{Item::}{layer.enabled}
60 property to true and then adjusting the \l{Item::}{layer.samples} property. In the
61 latter case, multi-sampling will not be applied to the entire scene, but the shape will be
62 rendered via an intermediate off-screen buffer. Alternatively, the
63 \l{QtQuick.Shapes.Shape::}{preferredRendererType} property can be set
64 to \c{Shape.CurveRenderer}. This has anti-aliasing built in and generally renders the shapes
65 at a higher quality, but at some additional performance cost.
66
67 For further information, the \l{Qt Quick Examples - Shapes}{Shapes example} shows how to
68 implement different types of shapes, fills and strokes, and the \l{Weather Forecast Example}
69 shows examples of different ways shapes might be useful in a user interface.
70*/
71
72void QQuickShapes_initializeModule()
73{
74 QQuickShapesModule::defineModule();
75}
76
78
79void QQuickShapesModule::defineModule()
80{
81 initResources();
82}
83
86 strokeWidth(1),
90 miterLimit(2),
93 dashOffset(0),
94 fillGradient(nullptr),
95 strokeGradient(nullptr),
96 fillItem(nullptr),
97 trim(nullptr)
98{
99 dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space
100}
101
102/*!
103 \qmltype ShapePath
104 //! \nativetype QQuickShapePath
105 \inqmlmodule QtQuick.Shapes
106 \ingroup qtquick-paths
107 \ingroup qtquick-views
108 \inherits Path
109 \brief Describes a Path and associated properties for stroking and filling.
110 \since 5.10
111
112 A \l Shape contains one or more ShapePath elements. At least one ShapePath is
113 necessary in order to have a Shape output anything visible. A ShapePath
114 itself is a \l Path with additional properties describing the stroking and
115 filling parameters, such as the stroke width and color, the fill color or
116 gradient, join and cap styles, and so on. As with ordinary \l Path objects,
117 ShapePath also contains a list of path elements like \l PathMove, \l PathLine,
118 \l PathCubic, \l PathQuad, \l PathArc, together with a starting position.
119
120 Any property changes in these data sets will be bubble up and change the
121 output of the Shape. This means that it is simple and easy to change, or
122 even animate, the starting and ending position, control points, or any
123 stroke or fill parameters using the usual QML bindings and animation types
124 like NumberAnimation.
125
126 In the following example the line join style changes automatically based on
127 the value of joinStyleIndex:
128
129 \qml
130 ShapePath {
131 strokeColor: "black"
132 strokeWidth: 16
133 fillColor: "transparent"
134 capStyle: ShapePath.RoundCap
135
136 property int joinStyleIndex: 0
137
138 property variant styles: [
139 ShapePath.BevelJoin,
140 ShapePath.MiterJoin,
141 ShapePath.RoundJoin
142 ]
143
144 joinStyle: styles[joinStyleIndex]
145
146 startX: 30
147 startY: 30
148 PathLine { x: 100; y: 100 }
149 PathLine { x: 30; y: 100 }
150 }
151 \endqml
152
153 Once associated with a Shape, here is the output with a joinStyleIndex
154 of 2 (ShapePath.RoundJoin):
155
156 \image visualpath-code-example.png {Black angled line with rounded corner}
157
158 \sa {Qt Quick Examples - Shapes}, {Weather Forecast Example}, Shape
159 */
160
161QQuickShapePathPrivate::QQuickShapePathPrivate()
162 : dirty(DirtyAll)
163{
164 // Set this QQuickPath to be a ShapePath
165 isShapePath = true;
166}
167
168QQuickShapePath::QQuickShapePath(QObject *parent)
169 : QQuickPath(*(new QQuickShapePathPrivate), parent)
170{
171 // The inherited changed() and the shapePathChanged() signals remain
172 // distinct, and this is intentional. Combining the two is not possible due
173 // to the difference in semantics and the need to act (see dirty flag
174 // below) differently on QQuickPath-related changes.
175
176 connect(this, &QQuickPath::changed, this, [this]() {
177 Q_D(QQuickShapePath);
178 d->dirty |= QQuickShapePathPrivate::DirtyPath;
179 emit shapePathChanged();
180 });
181}
182
183QQuickShapePath::~QQuickShapePath()
184{
185}
186
187/*!
188 \qmlproperty color QtQuick.Shapes::ShapePath::strokeColor
189
190 This property holds the stroking color.
191
192 When set to \c transparent, no stroking occurs. If the \l{strokeGradient} property is set,
193 it will take precedence over \c{strokeColor}.
194
195 The default value is \c white.
196 */
197
198QColor QQuickShapePath::strokeColor() const
199{
200 Q_D(const QQuickShapePath);
201 return d->sfp.strokeColor;
202}
203
204void QQuickShapePath::setStrokeColor(const QColor &color)
205{
206 Q_D(QQuickShapePath);
207 if (d->sfp.strokeColor != color) {
208 d->sfp.strokeColor = color;
209 d->dirty |= QQuickShapePathPrivate::DirtyStrokeColor;
210 emit strokeColorChanged();
211 emit shapePathChanged();
212 }
213}
214
215/*!
216 \qmlproperty real QtQuick.Shapes::ShapePath::strokeWidth
217
218 This property holds the stroke width.
219
220 When set to a negative value, no stroking occurs.
221
222 The default value is 1.
223
224 \sa cosmeticStroke
225 */
226
227qreal QQuickShapePath::strokeWidth() const
228{
229 Q_D(const QQuickShapePath);
230 return d->sfp.strokeWidth;
231}
232
233void QQuickShapePath::setStrokeWidth(qreal w)
234{
235 Q_D(QQuickShapePath);
236 if (d->sfp.strokeWidth != w) {
237 d->sfp.strokeWidth = w;
238 d->dirty |= QQuickShapePathPrivate::DirtyStrokeWidth;
239 emit strokeWidthChanged();
240 emit shapePathChanged();
241 }
242}
243
244/*! \since 6.11
245 \qmlproperty bool QtQuick.Shapes::ShapePath::cosmeticStroke
246
247 This property holds whether the stroke width remains constant despite rendering scale.
248
249 When this property is set to \c true, the outline of the shape
250 is drawn with constant width in \l {High DPI}{device-independent pixels},
251 as specified by \l strokeWidth, regardless of any transformations applied
252 to the shape, such as \l QtQuick::Item::scale.
253
254 The default value is \c false.
255
256 \sa strokeWidth
257*/
258bool QQuickShapePath::cosmeticStroke() const
259{
260 Q_D(const QQuickShapePath);
261 return d->sfp.cosmeticStroke;
262}
263
264void QQuickShapePath::setCosmeticStroke(bool c)
265{
266 Q_D(QQuickShapePath);
267 if (d->sfp.cosmeticStroke != c) {
268 d->sfp.cosmeticStroke = c;
269 d->dirty |= QQuickShapePathPrivate::DirtyStrokeWidth;
270 emit cosmeticStrokeChanged();
271 emit shapePathChanged();
272 }
273}
274
275/*!
276 \qmlproperty bool QtQuick.Shapes::ShapePath::visible
277 \since 6.13
278
279 This property holds whether the ShapePath is visible.
280
281 When set to \c false, neither the fill nor the stroke of this ShapePath is
282 rendered, and the ShapePath is ignored by \l {QQuickItem::contains()}{contains()}.
283 The ShapePath remains part of the \l Shape and keeps its position in the paint
284 order, so it is drawn at the same depth when made visible again, without having
285 to be re-created.
286
287 In contrast to a transparent ShapePath, a hidden ShapePath does not contribute
288 to \l {Shape::boundingRect}{Shape.boundingRect}, or to the implicit size of the
289 Shape. This means that changing the visibility of a ShapePath may cause the
290 Shape to change scale or position if a non-default fill mode or alignment is set.
291
292 The default value is \c true.
293*/
294bool QQuickShapePath::isVisible() const
295{
296 Q_D(const QQuickShapePath);
297 return d->sfp.visible;
298}
299
300void QQuickShapePath::setVisible(bool visible)
301{
302 Q_D(QQuickShapePath);
303 if (d->sfp.visible != visible) {
304 d->sfp.visible = visible;
305 d->dirty |= QQuickShapePathPrivate::DirtyVisible;
306 emit visibleChanged();
307 emit shapePathChanged();
308 }
309}
310
311/*!
312 \qmlproperty color QtQuick.Shapes::ShapePath::fillColor
313
314 This property holds the fill color.
315
316 When set to \c transparent, no filling occurs.
317
318 The default value is \c white.
319
320 \note If either \l fillGradient or \l fillItem are set to something other than \c null, these
321 will take precedence over \c fillColor. The \c fillColor will be ignored in this case.
322 */
323
324QColor QQuickShapePath::fillColor() const
325{
326 Q_D(const QQuickShapePath);
327 return d->sfp.fillColor;
328}
329
330void QQuickShapePath::setFillColor(const QColor &color)
331{
332 Q_D(QQuickShapePath);
333 if (d->sfp.fillColor != color) {
334 d->sfp.fillColor = color;
335 d->dirty |= QQuickShapePathPrivate::DirtyFillColor;
336 emit fillColorChanged();
337 emit shapePathChanged();
338 }
339}
340
341/*!
342 \include shapepath.qdocinc {fillRule-property} {QtQuick.Shapes::ShapePath}
343*/
344
345QQuickShapePath::FillRule QQuickShapePath::fillRule() const
346{
347 Q_D(const QQuickShapePath);
348 return d->sfp.fillRule;
349}
350
351void QQuickShapePath::setFillRule(FillRule fillRule)
352{
353 Q_D(QQuickShapePath);
354 if (d->sfp.fillRule != fillRule) {
355 d->sfp.fillRule = fillRule;
356 d->dirty |= QQuickShapePathPrivate::DirtyFillRule;
357 emit fillRuleChanged();
358 emit shapePathChanged();
359 }
360}
361
362/*!
363 \include shapepath.qdocinc {joinStyle-property} {QtQuick.Shapes::ShapePath}
364*/
365
366QQuickShapePath::JoinStyle QQuickShapePath::joinStyle() const
367{
368 Q_D(const QQuickShapePath);
369 return d->sfp.joinStyle;
370}
371
372void QQuickShapePath::setJoinStyle(JoinStyle style)
373{
374 Q_D(QQuickShapePath);
375 if (d->sfp.joinStyle != style) {
376 d->sfp.joinStyle = style;
377 d->dirty |= QQuickShapePathPrivate::DirtyStyle;
378 emit joinStyleChanged();
379 emit shapePathChanged();
380 }
381}
382
383/*!
384 \qmlproperty int QtQuick.Shapes::ShapePath::miterLimit
385
386 When joinStyle is set to \c ShapePath.MiterJoin, this property
387 specifies how far the miter join can extend from the join point.
388
389 The default value is 2.
390 */
391
392int QQuickShapePath::miterLimit() const
393{
394 Q_D(const QQuickShapePath);
395 return d->sfp.miterLimit;
396}
397
398void QQuickShapePath::setMiterLimit(int limit)
399{
400 Q_D(QQuickShapePath);
401 if (d->sfp.miterLimit != limit) {
402 d->sfp.miterLimit = limit;
403 d->dirty |= QQuickShapePathPrivate::DirtyStyle;
404 emit miterLimitChanged();
405 emit shapePathChanged();
406 }
407}
408
409/*!
410 \include shapepath.qdocinc {capStyle-property} {QtQuick.Shapes::ShapePath}
411*/
412
413QQuickShapePath::CapStyle QQuickShapePath::capStyle() const
414{
415 Q_D(const QQuickShapePath);
416 return d->sfp.capStyle;
417}
418
419void QQuickShapePath::setCapStyle(CapStyle style)
420{
421 Q_D(QQuickShapePath);
422 if (d->sfp.capStyle != style) {
423 d->sfp.capStyle = style;
424 d->dirty |= QQuickShapePathPrivate::DirtyStyle;
425 emit capStyleChanged();
426 emit shapePathChanged();
427 }
428}
429
430/*!
431 \include shapepath.qdocinc {strokeStyle-property} {QtQuick.Shapes::ShapePath}
432*/
433
434QQuickShapePath::StrokeStyle QQuickShapePath::strokeStyle() const
435{
436 Q_D(const QQuickShapePath);
437 return d->sfp.strokeStyle;
438}
439
440void QQuickShapePath::setStrokeStyle(StrokeStyle style)
441{
442 Q_D(QQuickShapePath);
443 if (d->sfp.strokeStyle != style) {
444 d->sfp.strokeStyle = style;
445 d->dirty |= QQuickShapePathPrivate::DirtyDash;
446 emit strokeStyleChanged();
447 emit shapePathChanged();
448 }
449}
450
451/*!
452 \include shapepath.qdocinc {dashOffset-property} {QtQuick.Shapes::ShapePath}
453*/
454
455qreal QQuickShapePath::dashOffset() const
456{
457 Q_D(const QQuickShapePath);
458 return d->sfp.dashOffset;
459}
460
461void QQuickShapePath::setDashOffset(qreal offset)
462{
463 Q_D(QQuickShapePath);
464 if (d->sfp.dashOffset != offset) {
465 d->sfp.dashOffset = offset;
466 d->dirty |= QQuickShapePathPrivate::DirtyDash;
467 emit dashOffsetChanged();
468 emit shapePathChanged();
469 }
470}
471
472/*!
473 \include shapepath.qdocinc {dashPattern-property} {QtQuick.Shapes::ShapePath}
474*/
475
476QList<qreal> QQuickShapePath::dashPattern() const
477{
478 Q_D(const QQuickShapePath);
479 return d->sfp.dashPattern;
480}
481
482void QQuickShapePath::setDashPattern(const QList<qreal> &array)
483{
484 Q_D(QQuickShapePath);
485 if (d->sfp.dashPattern != array) {
486 d->sfp.dashPattern = array;
487 d->dirty |= QQuickShapePathPrivate::DirtyDash;
488 emit dashPatternChanged();
489 emit shapePathChanged();
490 }
491}
492
493/*!
494 \qmlproperty ShapeGradient QtQuick.Shapes::ShapePath::strokeGradient
495 \since 6.12
496
497 This property defines the stroke gradient. By default no gradient is enabled
498 and the value is \c null. In this case the stroke will be based \l{strokeColor} property.
499
500 \note The Gradient type cannot be used here. Rather, prefer using one of
501 the advanced subtypes, like LinearGradient.
502
503 \note If set to something other than \c{null}, the \c strokeGradient will take precedence over
504 \l strokeColor.
505
506 By default, up to 256 different gradients may be displayed simultaneously. This limit may be
507 customized with the \c QT_QUICKSHAPES_MAX_GRADIENTS environment variable.
508 */
509QQuickShapeGradient *QQuickShapePath::strokeGradient() const
510{
511 Q_D(const QQuickShapePath);
512 return d->sfp.strokeGradient;
513}
514
515void QQuickShapePath::setStrokeGradient(QQuickShapeGradient *gradient)
516{
517 Q_D(QQuickShapePath);
518 if (d->sfp.strokeGradient != gradient) {
519 if (d->sfp.strokeGradient)
520 qmlobject_disconnect(d->sfp.strokeGradient, QQuickShapeGradient, SIGNAL(updated()),
521 this, QQuickShapePath, SLOT(_q_strokeGradientChanged()));
522 d->sfp.strokeGradient = gradient;
523 if (d->sfp.strokeGradient)
524 qmlobject_connect(d->sfp.strokeGradient, QQuickShapeGradient, SIGNAL(updated()),
525 this, QQuickShapePath, SLOT(_q_strokeGradientChanged()));
526 emit strokeGradientChanged();
527 d->dirty |= QQuickShapePathPrivate::DirtyStrokeGradient;
528 emit shapePathChanged();
529 }
530}
531
532void QQuickShapePath::resetStrokeGradient()
533{
534 setStrokeGradient(nullptr);
535}
536
537void QQuickShapePathPrivate::_q_strokeGradientChanged()
538{
539 Q_Q(QQuickShapePath);
540 dirty |= DirtyStrokeGradient;
541 emit q->shapePathChanged();
542}
543
544/*!
545 \qmlproperty ShapeGradient QtQuick.Shapes::ShapePath::fillGradient
546
547 This property defines the fill gradient. By default no gradient is enabled
548 and the value is \c null. In this case the fill will either be based on the \l fillItem
549 property if it is set, and otherwise the \l{fillColor} property will be used.
550
551 \note The Gradient type cannot be used here. Rather, prefer using one of
552 the advanced subtypes, like LinearGradient.
553
554 \note If set to something other than \c{null}, the \c fillGradient will take precedence over
555 both \l fillItem and \l fillColor.
556
557 By default, up to 256 different gradients may be displayed simultaneously. This limit may be
558 customized with the \c QT_QUICKSHAPES_MAX_GRADIENTS environment variable.
559 */
560
561QQuickShapeGradient *QQuickShapePath::fillGradient() const
562{
563 Q_D(const QQuickShapePath);
564 return d->sfp.fillGradient;
565}
566
567void QQuickShapePath::setFillGradient(QQuickShapeGradient *gradient)
568{
569 Q_D(QQuickShapePath);
570 if (d->sfp.fillGradient != gradient) {
571 if (d->sfp.fillGradient)
572 qmlobject_disconnect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
573 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
574 d->sfp.fillGradient = gradient;
575 if (d->sfp.fillGradient)
576 qmlobject_connect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
577 this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
578 emit fillGradientChanged();
579 d->dirty |= QQuickShapePathPrivate::DirtyFillGradient;
580 emit shapePathChanged();
581 }
582}
583
584void QQuickShapePath::resetFillGradient()
585{
586 setFillGradient(nullptr);
587}
588
589void QQuickShapePathPrivate::_q_fillGradientChanged()
590{
591 Q_Q(QQuickShapePath);
592 dirty |= DirtyFillGradient;
593 emit q->shapePathChanged();
594}
595
596/*!
597 \qmlproperty Item QtQuick.Shapes::ShapePath::fillItem
598 \since 6.8
599
600 This property defines another Qt Quick Item to use as fill by the shape. The item must be
601 texture provider (such as a \l {Item Layers} {layered item}, a \l{ShaderEffectSource} or an
602 \l{Image}). If it is not a valid texture provider, this property will be ignored.
603
604 The visual parent of \c fillItem must be a Qt Quick \l{Item}. In particular, since \c{ShapePath}
605 is not an \l{Item}, its children cannot be used as fill items. Manually setting the
606 \c{fillItem}'s parent is needed when it is created as a child of the \c{ShapePath}.
607
608 For instance, creating an \l{Image} object directly in the \c{fillItem} property assignment will
609 make it a child of the \c{ShapePath}. In this case, its parent must be set manually. In the
610 following example we use the window's \l{Window::contentItem}{contentItem} as the parent.
611
612 \code
613 fillItem: Image {
614 visible: false
615 source: "contents.png"
616 parent: window.contentItem
617 }
618 \endcode
619
620 \note When using a layered item as a \c fillItem, you may see pixelation effects when
621 transforming the fill. Setting the \l {QtQuick::Item::}{layer.smooth} property to true will
622 give better visual results in this case.
623
624 By default no fill item is set and the value is \c null.
625
626 \note If set to something other than \c null, the \c fillItem property takes precedence over
627 \l fillColor. The \l fillGradient property in turn takes precedence over both \c fillItem and
628 \l{fillColor}.
629 */
630
631QQuickItem *QQuickShapePath::fillItem() const
632{
633 Q_D(const QQuickShapePath);
634 return d->sfp.fillItem;
635}
636
637void QQuickShapePath::setFillItem(QQuickItem *fillItem)
638{
639 Q_D(QQuickShapePath);
640 if (d->sfp.fillItem != fillItem) {
641 if (d->sfp.fillItem != nullptr) {
642 qmlobject_disconnect(d->sfp.fillItem, QQuickItem, SIGNAL(destroyed()),
643 this, QQuickShapePath, SLOT(_q_fillItemDestroyed()));
644 }
645 d->sfp.fillItem = fillItem;
646 if (d->sfp.fillItem != nullptr) {
647 qmlobject_connect(d->sfp.fillItem, QQuickItem, SIGNAL(destroyed()),
648 this, QQuickShapePath, SLOT(_q_fillItemDestroyed()));
649 }
650 emit fillItemChanged();
651
652 d->dirty |= QQuickShapePathPrivate::DirtyFillItem;
653 emit shapePathChanged();
654 }
655}
656
657/*!
658 \qmlpropertygroup QtQuick.Shapes::ShapePath::trim
659 \qmlproperty real QtQuick.Shapes::ShapePath::trim.start
660 \qmlproperty real QtQuick.Shapes::ShapePath::trim.end
661 \qmlproperty real QtQuick.Shapes::ShapePath::trim.offset
662 \since 6.10
663
664 Specifies the section of this path that will be displayed.
665
666 The section is defined by the path length fractions \c start and \c end. By default, \c start
667 is 0 (denoting the start of the path) and \c end is 1 (denoting the end of the path), so the
668 entire path is displayed.
669
670 The value of \c offset is added to \c start and \c end. If that causes over- or underrun of the
671 [0, 1] range, the values will be wrapped around, as will the resulting path section. The
672 effective range of \c offset is between -1 and 1. The default value is 0.
673*/
674
675QQuickShapeTrim *QQuickShapePath::trim()
676{
677 Q_D(QQuickShapePath);
678 if (!d->sfp.trim) {
679 d->sfp.trim = new QQuickShapeTrim;
680 QQml_setParent_noEvent(d->sfp.trim, this);
681 }
682 return d->sfp.trim;
683}
684
685bool QQuickShapePath::hasTrim() const
686{
687 Q_D(const QQuickShapePath);
688 return d->sfp.trim != nullptr;
689}
690
691void QQuickShapePathPrivate::_q_fillItemDestroyed()
692{
693 Q_Q(QQuickShapePath);
694 sfp.fillItem = nullptr;
695 dirty |= DirtyFillItem;
696 emit q->fillItemChanged();
697 emit q->shapePathChanged();
698}
699
700#ifndef QT_NO_DEBUG_STREAM
701void QQuickShapePathPrivate::writeToDebugStream(QDebug &debug) const
702{
703 debug.nospace() << "QQuickShapePath(" << (const void *)this
704 << " startX=" << startX
705 << " startY=" << startY
706 << " _pathElements=" << _pathElements
707 << ')';
708}
709#endif
710
711/*!
712 \qmlproperty PathHints QtQuick.Shapes::ShapePath::pathHints
713 \since 6.7
714
715 This property describes characteristics of the shape. If set, these hints may allow
716 optimized rendering. By default, no hints are set. It can be a combination of the following
717 values:
718
719 \value ShapePath.PathLinear
720 The path only has straight lines, no curves.
721 \value ShapePath.PathQuadratic
722 The path does not have any cubic curves: only lines and quadratic Bezier curves.
723 \value ShapePath.PathConvex
724 The path does not have any dents or holes. All straight lines between two points
725 inside the shape will be completely inside the shape.
726 \value ShapePath.PathFillOnRight
727 The path follows the TrueType convention where outlines around solid fill have their
728 control points ordered clockwise, and outlines around holes in the shape have their
729 control points ordered counter-clockwise.
730 \value ShapePath.PathSolid
731 The path has no holes, or mathematically speaking it is \e{simply connected}.
732 \value ShapePath.PathNonIntersecting
733 The path outline does not cross itself.
734 \value ShapePath.PathNonOverlappingControlPointTriangles
735 The triangles defined by the curve control points do not overlap with each other,
736 or with any of the line segments. Also, no line segments intersect.
737 This implies \c PathNonIntersecting.
738
739 Not all hints are logically independent, but the dependencies are not enforced.
740 For example, \c PathLinear implies \c PathQuadratic, but it is valid to have \c PathLinear
741 without \c PathQuadratic.
742
743 The pathHints property describes a set of statements known to be true; the absence of a hint
744 does not necessarily mean that the corresponding statement is false.
745*/
746
747QQuickShapePath::PathHints QQuickShapePath::pathHints() const
748{
749 Q_D(const QQuickShapePath);
750 return d->pathHints;
751}
752
753void QQuickShapePath::setPathHints(PathHints newPathHints)
754{
755 Q_D(QQuickShapePath);
756 if (d->pathHints == newPathHints)
757 return;
758 d->pathHints = newPathHints;
759 emit pathHintsChanged();
760}
761
762/*!
763 \qmlproperty matrix4x4 QtQuick.Shapes::ShapePath::fillTransform
764 \since 6.8
765
766 This property defines a transform to be applied to the path's fill pattern (\l fillGradient or
767 \l fillItem). It has no effect if the fill is a solid color or transparent. By default no fill
768 transform is enabled and the value of this property is the \c identity matrix.
769
770 This example displays a rectangle filled with the contents of \c myImageItem rotated 45 degrees
771 around the center point of \c myShape:
772
773 \qml
774 ShapePath {
775 fillItem: myImageItem
776 fillTransform: PlanarTransform.fromRotate(45, myShape.width / 2, myShape.height / 2)
777 PathRectangle { x: 10; y: 10; width: myShape.width - 20; height: myShape.height - 20 }
778 }
779 \endqml
780*/
781
782QMatrix4x4 QQuickShapePath::fillTransform() const
783{
784 Q_D(const QQuickShapePath);
785 return d->sfp.fillTransform.matrix();
786}
787
788void QQuickShapePath::setFillTransform(const QMatrix4x4 &matrix)
789{
790 Q_D(QQuickShapePath);
791 if (d->sfp.fillTransform != matrix) {
792 d->sfp.fillTransform.setMatrix(matrix);
793 d->dirty |= QQuickShapePathPrivate::DirtyFillTransform;
794 emit fillTransformChanged();
795 emit shapePathChanged();
796 }
797}
798
799
800QQuickShapeTrim::QQuickShapeTrim(QObject *parent)
801 : QObject(parent)
802{
803}
804
805qreal QQuickShapeTrim::start() const
806{
807 return m_start;
808}
809
810void QQuickShapeTrim::setStart(qreal t)
811{
812 if (t == m_start)
813 return;
814 m_start = t;
815 QQuickShapePath *shapePath = qobject_cast<QQuickShapePath *>(parent());
816 if (shapePath) {
817 QQuickShapePathPrivate *d = QQuickShapePathPrivate::get(shapePath);
818 d->dirty |= QQuickShapePathPrivate::DirtyTrim;
819 emit startChanged();
820 emit shapePath->shapePathChanged();
821 }
822}
823
824qreal QQuickShapeTrim::end() const
825{
826 return m_end;
827}
828
829void QQuickShapeTrim::setEnd(qreal t)
830{
831 if (t == m_end)
832 return;
833 m_end = t;
834 QQuickShapePath *shapePath = qobject_cast<QQuickShapePath *>(parent());
835 if (shapePath) {
836 QQuickShapePathPrivate *d = QQuickShapePathPrivate::get(shapePath);
837 d->dirty |= QQuickShapePathPrivate::DirtyTrim;
838 emit endChanged();
839 emit shapePath->shapePathChanged();
840 }
841}
842
843qreal QQuickShapeTrim::offset() const
844{
845 return m_offset;
846}
847
848void QQuickShapeTrim::setOffset(qreal t)
849{
850 if (t == m_offset)
851 return;
852 m_offset = t;
853 QQuickShapePath *shapePath = qobject_cast<QQuickShapePath *>(parent());
854 if (shapePath) {
855 QQuickShapePathPrivate *d = QQuickShapePathPrivate::get(shapePath);
856 d->dirty |= QQuickShapePathPrivate::DirtyTrim;
857 emit offsetChanged();
858 emit shapePath->shapePathChanged();
859 }
860}
861
862/*!
863 \qmltype Shape
864 //! \nativetype QQuickShape
865 \inqmlmodule QtQuick.Shapes
866 \ingroup qtquick-paths
867 \ingroup qtquick-views
868 \inherits Item
869 \brief Renders a path.
870 \since 5.10
871
872 Renders a path by triangulating geometry from a QPainterPath.
873
874 This approach is different from rendering shapes via QQuickPaintedItem or
875 the 2D Canvas because the path never gets rasterized in software.
876 Therefore Shape is suitable for creating shapes spreading over larger
877 areas of the screen, avoiding the performance penalty for texture uploads
878 or framebuffer blits. In addition, the declarative API allows manipulating,
879 binding to, and even animating the path element properties like starting
880 and ending position, the control points, and so on.
881
882 The types for specifying path elements are shared between \l PathView and
883 Shape. However, not all Shape implementations support all path
884 element types, while some may not make sense for PathView. Shape's
885 currently supported subset is: PathMove, PathLine, PathQuad, PathCubic,
886 PathArc, PathText and PathSvg.
887
888 See \l Path for a detailed overview of the supported path elements.
889
890 \qml
891 Shape {
892 width: 200
893 height: 150
894 anchors.centerIn: parent
895 ShapePath {
896 strokeWidth: 4
897 strokeColor: "red"
898 fillGradient: LinearGradient {
899 x1: 20; y1: 20
900 x2: 180; y2: 130
901 GradientStop { position: 0; color: "blue" }
902 GradientStop { position: 0.2; color: "green" }
903 GradientStop { position: 0.4; color: "red" }
904 GradientStop { position: 0.6; color: "yellow" }
905 GradientStop { position: 1; color: "cyan" }
906 }
907 strokeStyle: ShapePath.DashLine
908 dashPattern: [ 1, 4 ]
909 startX: 20; startY: 20
910 PathLine { x: 180; y: 130 }
911 PathLine { x: 20; y: 130 }
912 PathLine { x: 20; y: 20 }
913 }
914 }
915 \endqml
916
917 \image pathitem-code-example.png
918 {Triangle with rainbow gradient fill and red dashed stroke}
919
920 Like \l Item, Shape also allows any visual or non-visual objects to be
921 declared as children. ShapePath objects are handled specially. This is
922 useful since it allows adding visual items, like \l Rectangle or \l Image,
923 and non-visual objects, like \l Timer directly as children of Shape.
924
925 The following list summarizes the available Shape rendering approaches:
926
927 \list
928
929 \li When Qt Quick is running with the default, hardware-accelerated backend (RHI),
930 the generic shape renderer will be used. This converts the shapes into triangles
931 which are passed to the renderer.
932
933 \li The \c software backend is fully supported. The path is rendered via
934 QPainter::strokePath() and QPainter::fillPath() in this case.
935
936 \li The OpenVG backend is not currently supported.
937
938 \endlist
939
940 When using Shape, it is important to be aware of potential performance
941 implications:
942
943 \list
944
945 \li When the application is running with the generic, triangulation-based
946 Shape implementation, the geometry generation happens entirely on the
947 CPU. This is potentially expensive. Changing the set of path elements,
948 changing the properties of these elements, or changing certain properties
949 of the Shape itself all lead to retriangulation of the affected paths on
950 every change. Therefore, applying animation to such properties can affect
951 performance on less powerful systems.
952
953 \li However, the data-driven, declarative nature of the Shape API often
954 means better cacheability for the underlying CPU and GPU resources. A
955 property change in one ShapePath will only lead to reprocessing the
956 affected ShapePath, leaving other parts of the Shape unchanged. Therefore,
957 a frequently changing property can still result in a lower overall system
958 load than with imperative painting approaches (for example, QPainter).
959
960 \li At the same time, attention must be paid to the number of Shape
961 elements in the scene. The way such a Shape item is represented in
962 the scene graph is different from an ordinary geometry-based item,
963 and incurs a certain cost when it comes to OpenGL state changes.
964
965 \li As a general rule, scenes should avoid using separate Shape items when
966 it is not absolutely necessary. Prefer using one Shape item with multiple
967 ShapePath elements over multiple Shape items.
968
969 \endlist
970
971 \sa {Qt Quick Examples - Shapes}, {Weather Forecast Example}, Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg
972*/
973
974QQuickShapePrivate::QQuickShapePrivate()
975 : effectRefCount(0)
976{
977}
978
979QQuickShapePrivate::~QQuickShapePrivate()
980{
981 delete renderer;
982}
983
984void QQuickShapePrivate::init()
985{
986 Q_Q(QQuickShape);
987 q->setFlag(QQuickItem::ItemHasContents);
988}
989
990void QQuickShapePrivate::_q_shapePathChanged()
991{
992 Q_Q(QQuickShape);
993 spChanged = true;
994 q->polish();
995 emit q->boundingRectChanged();
996 auto br = q->boundingRect();
997 q->setImplicitSize(br.right(), br.bottom());
998}
999
1000void QQuickShapePrivate::handleSceneChange(QQuickWindow *w)
1001{
1002 if (renderer != nullptr)
1003 renderer->handleSceneChange(w);
1004}
1005
1006void QQuickShapePrivate::setStatus(QQuickShape::Status newStatus)
1007{
1008 Q_Q(QQuickShape);
1009 if (status != newStatus) {
1010 status = newStatus;
1011 emit q->statusChanged();
1012 }
1013}
1014
1015qreal QQuickShapePrivate::getImplicitWidth() const
1016{
1017 Q_Q(const QQuickShape);
1018 return q->boundingRect().right();
1019}
1020
1021qreal QQuickShapePrivate::getImplicitHeight() const
1022{
1023 Q_Q(const QQuickShape);
1024 return q->boundingRect().bottom();
1025}
1026
1027QQuickShape::QQuickShape(QQuickItem *parent)
1028 : QQuickItem(*(new QQuickShapePrivate), parent)
1029{
1030 Q_D(QQuickShape);
1031 d->init();
1032}
1033
1034QQuickShape::QQuickShape(QQuickShapePrivate &dd, QQuickItem *parent)
1035 : QQuickItem(dd, parent)
1036{
1037 Q_D(QQuickShape);
1038 d->init();
1039}
1040
1041QQuickShape::~QQuickShape()
1042{
1043}
1044
1045/*!
1046 \qmlproperty enumeration QtQuick.Shapes::Shape::rendererType
1047 \readonly
1048
1049 This property determines which path rendering backend is active.
1050
1051 \value Shape.UnknownRenderer
1052 The renderer is unknown.
1053
1054 \value Shape.GeometryRenderer
1055 The generic, driver independent solution for GPU rendering. Uses the same
1056 CPU-based triangulation approach as QPainter's OpenGL 2 paint
1057 engine. This is the default when the RHI-based Qt Quick scenegraph
1058 backend is in use.
1059
1060 \value Shape.SoftwareRenderer
1061 Pure QPainter drawing using the raster paint engine. This is the
1062 default, and only, option when the Qt Quick scenegraph is running
1063 with the \c software backend.
1064
1065 \value Shape.CurveRenderer
1066 GPU-based renderer that aims to preserve curvature at any scale.
1067 In contrast to \c Shape.GeometryRenderer, curves are not approximated by short straight
1068 lines. Instead, curves are rendered using a specialized fragment shader. This improves
1069 visual quality and avoids re-tesselation performance hit when zooming. Also,
1070 \c Shape.CurveRenderer provides native, high-quality anti-aliasing, without the
1071 performance cost of multi- or supersampling.
1072
1073 By default, \c Shape.GeometryRenderer will be selected unless the Qt Quick scenegraph is running
1074 with the \c software backend. In that case, \c Shape.SoftwareRenderer will be used.
1075 \c Shape.CurveRenderer may be requested using the \l preferredRendererType property.
1076
1077 \note The \c Shape.CurveRenderer will approximate cubic curves with quadratic ones and may
1078 therefore diverge slightly from the mathematically correct visualization of the shape. In
1079 addition, if the shape is being rendered into a Qt Quick 3D scene and the OpenGL backend for
1080 RHI is active, the \c GL_OES_standard_derivatives extension to OpenGL is required (this is
1081 available by default on OpenGL ES 3 and later, but optional in OpenGL ES 2.)
1082*/
1083
1084QQuickShape::RendererType QQuickShape::rendererType() const
1085{
1086 Q_D(const QQuickShape);
1087 return d->rendererType;
1088}
1089
1090/*!
1091 \qmlproperty enumeration QtQuick.Shapes::Shape::preferredRendererType
1092 \since 6.6
1093
1094 Requests a specific backend to use for rendering the shape. The possible values are the same as
1095 for \l rendererType. The default is \c Shape.UnknownRenderer, indicating no particular preference.
1096
1097 If the requested renderer type is not supported for the current Qt Quick backend, the default
1098 renderer for that backend will be used instead. This will be reflected in the \l rendererType
1099 when the backend is initialized.
1100
1101 \c Shape.SoftwareRenderer can currently not be selected without running the scenegraph with
1102 the \c software backend, in which case it will be selected regardless of the
1103 \c preferredRendererType.
1104
1105 See \l rendererType for more information on the implications.
1106*/
1107
1108QQuickShape::RendererType QQuickShape::preferredRendererType() const
1109{
1110 Q_D(const QQuickShape);
1111 return d->preferredType;
1112}
1113
1114void QQuickShape::setPreferredRendererType(QQuickShape::RendererType preferredType)
1115{
1116 Q_D(QQuickShape);
1117 if (d->preferredType == preferredType)
1118 return;
1119
1120 d->preferredType = preferredType;
1121 // (could bail out here if selectRenderType shows no change?)
1122
1123 for (int i = 0; i < d->sp.size(); ++i) {
1124 QQuickShapePath *p = d->sp[i];
1125 QQuickShapePathPrivate *pp = QQuickShapePathPrivate::get(p);
1126 pp->dirty |= QQuickShapePathPrivate::DirtyAll;
1127 }
1128 d->spChanged = true;
1129 d->_q_shapePathChanged();
1130 polish();
1131 update();
1132
1133 emit preferredRendererTypeChanged();
1134}
1135
1136/*!
1137 \qmlproperty bool QtQuick.Shapes::Shape::asynchronous
1138
1139 When rendererType is \c Shape.GeometryRenderer or \c Shape.CurveRenderer, a certain amount of
1140 preprocessing of the input path is performed on the CPU during the polishing phase of the
1141 Shape. This is potentially expensive. To offload this work to separate worker threads, set this
1142 property to \c true.
1143
1144 When enabled, making a Shape visible will not wait for the content to
1145 become available. Instead, the GUI/main thread is not blocked and the
1146 results of the path rendering are shown only when all the asynchronous
1147 work has been finished.
1148
1149 The default value is \c false.
1150 */
1151
1152bool QQuickShape::asynchronous() const
1153{
1154 Q_D(const QQuickShape);
1155 return d->async;
1156}
1157
1158void QQuickShape::setAsynchronous(bool async)
1159{
1160 Q_D(QQuickShape);
1161 if (d->async != async) {
1162 d->async = async;
1163 emit asynchronousChanged();
1164 if (d->componentComplete)
1165 d->_q_shapePathChanged();
1166 }
1167}
1168
1169/*!
1170 \qmlproperty rect QtQuick.Shapes::Shape::boundingRect
1171 \readonly
1172 \since 6.6
1173
1174 Contains the united bounding rect of all visible sub paths in the shape.
1175
1176 A \l ShapePath whose \l {ShapePath::visible}{visible} property is \c false
1177 does not contribute.
1178 */
1179QRectF QQuickShape::boundingRect() const
1180{
1181 Q_D(const QQuickShape);
1182 QRectF brect;
1183 for (QQuickShapePath *path : d->sp) {
1184 if (!path->isVisible())
1185 continue;
1186 qreal pw = path->strokeColor().alpha() ? path->strokeWidth() : 0;
1187 qreal d = path->capStyle() == QQuickShapePath::SquareCap ? pw * M_SQRT1_2 : pw / 2;
1188 brect = brect.united(path->path().boundingRect().adjusted(-d, -d, d, d));
1189 }
1190
1191 return brect;
1192}
1193
1194/*!
1195 \qmlproperty bool QtQuick.Shapes::Shape::vendorExtensionsEnabled
1196
1197 This property controls the usage of non-standard OpenGL extensions.
1198
1199 The default value is \c false.
1200
1201 As of Qt 6.0 there are no vendor-specific rendering paths implemented.
1202 */
1203
1204bool QQuickShape::vendorExtensionsEnabled() const
1205{
1206 Q_D(const QQuickShape);
1207 return d->enableVendorExts;
1208}
1209
1210void QQuickShape::setVendorExtensionsEnabled(bool enable)
1211{
1212 Q_D(QQuickShape);
1213 if (d->enableVendorExts != enable) {
1214 d->enableVendorExts = enable;
1215 emit vendorExtensionsEnabledChanged();
1216 }
1217}
1218
1219/*!
1220 \qmlproperty enumeration QtQuick.Shapes::Shape::status
1221 \readonly
1222
1223 This property determines the status of the Shape and is relevant when
1224 Shape.asynchronous is set to \c true.
1225
1226 \value Shape.Null
1227 Not yet initialized.
1228
1229 \value Shape.Ready
1230 The Shape has finished processing.
1231
1232 \value Shape.Processing
1233 The path is being processed.
1234 */
1235
1236QQuickShape::Status QQuickShape::status() const
1237{
1238 Q_D(const QQuickShape);
1239 return d->status;
1240}
1241
1242/*!
1243 \qmlproperty enumeration QtQuick.Shapes::Shape::containsMode
1244 \since QtQuick.Shapes 1.11
1245
1246 This property determines the definition of \l {QQuickItem::contains()}{contains()}
1247 for the Shape. It is useful in case you add \l {Qt Quick Input Handlers} and you want to
1248 react only when the mouse or touchpoint is fully inside the Shape.
1249
1250 \value Shape.BoundingRectContains
1251 The default implementation of \l QQuickItem::contains() checks only
1252 whether the given point is inside the rectangular bounding box. This is
1253 the most efficient implementation, which is why it's the default.
1254
1255 \value Shape.FillContains
1256 Check whether the interior (the part that would be filled if you are
1257 rendering it with fill) of any visible \l ShapePath that makes up this
1258 Shape contains the given point. The more complex and numerous ShapePaths you
1259 add, the less efficient this is to check, which can potentially slow
1260 down event delivery in your application. So it should be used with care.
1261
1262 One way to speed up the \c FillContains check is to generate an approximate
1263 outline with as few points as possible, place that in a transparent Shape
1264 on top, and add your Pointer Handlers to that, so that the containment
1265 check is cheaper during event delivery.
1266*/
1267QQuickShape::ContainsMode QQuickShape::containsMode() const
1268{
1269 Q_D(const QQuickShape);
1270 return d->containsMode;
1271}
1272
1273void QQuickShape::setContainsMode(QQuickShape::ContainsMode containsMode)
1274{
1275 Q_D(QQuickShape);
1276 if (d->containsMode == containsMode)
1277 return;
1278
1279 d->containsMode = containsMode;
1280 emit containsModeChanged();
1281}
1282
1283bool QQuickShape::contains(const QPointF &point) const
1284{
1285 Q_D(const QQuickShape);
1286 switch (d->containsMode) {
1287 case BoundingRectContains:
1288 return QQuickItem::contains(point);
1289 case FillContains:
1290 for (QQuickShapePath *path : d->sp) {
1291 if (path->isVisible() && path->path().contains(point))
1292 return true;
1293 }
1294 }
1295 return false;
1296}
1297
1298/*!
1299 \qmlproperty enumeration QtQuick.Shapes::Shape::fillMode
1300 \since QtQuick.Shapes 6.7
1301
1302 Set this property to define what happens when the path has a different size
1303 than the item.
1304
1305 \value Shape.NoResize the shape is rendered at its native size, independent of the size of the item. This is the default
1306 \value Shape.Stretch the shape is scaled to fit the item, changing the aspect ratio if necessary.
1307 Note that non-uniform scaling may cause reduced quality of anti-aliasing when using the curve renderer
1308 \value Shape.PreserveAspectFit the shape is scaled uniformly to fit inside the item
1309 \value Shape.PreserveAspectCrop the shape is scaled uniformly to fill the item fully, extending outside the item if necessary.
1310 Note that this only actually crops the content if \l clip is true
1311*/
1312
1313QQuickShape::FillMode QQuickShape::fillMode() const
1314{
1315 Q_D(const QQuickShape);
1316 return d->fillMode;
1317}
1318
1319void QQuickShape::setFillMode(FillMode newFillMode)
1320{
1321 Q_D(QQuickShape);
1322 if (d->fillMode == newFillMode)
1323 return;
1324 d->fillMode = newFillMode;
1325 emit fillModeChanged();
1326}
1327
1328/*!
1329 \qmlproperty enumeration QtQuick.Shapes::Shape::horizontalAlignment
1330 \qmlproperty enumeration QtQuick.Shapes::Shape::verticalAlignment
1331 \since 6.7
1332
1333 Sets the horizontal and vertical alignment of the shape within the item.
1334 By default, the shape is aligned with \c{(0,0)} on the top left corner.
1335
1336 The valid values for \c horizontalAlignment are \c Shape.AlignLeft,
1337 \c Shape.AlignRight and \c Shape.AlignHCenter. The valid values for
1338 \c verticalAlignment are \c Shape.AlignTop, \c Shape.AlignBottom and
1339 \c Shape.AlignVCenter.
1340*/
1341
1342QQuickShape::HAlignment QQuickShape::horizontalAlignment() const
1343{
1344 Q_D(const QQuickShape);
1345 return d->horizontalAlignment;
1346}
1347
1348void QQuickShape::setHorizontalAlignment(HAlignment newHorizontalAlignment)
1349{
1350 Q_D(QQuickShape);
1351 if (d->horizontalAlignment == newHorizontalAlignment)
1352 return;
1353 d->horizontalAlignment = newHorizontalAlignment;
1354 emit horizontalAlignmentChanged();
1355}
1356
1357QQuickShape::VAlignment QQuickShape::verticalAlignment() const
1358{
1359 Q_D(const QQuickShape);
1360 return d->verticalAlignment;
1361}
1362
1363void QQuickShape::setVerticalAlignment(VAlignment newVerticalAlignment)
1364{
1365 Q_D(QQuickShape);
1366 if (d->verticalAlignment == newVerticalAlignment)
1367 return;
1368 d->verticalAlignment = newVerticalAlignment;
1369 emit verticalAlignmentChanged();
1370}
1371
1372static void vpe_append(QQmlListProperty<QObject> *property, QObject *obj)
1373{
1374 QQuickShape *item = static_cast<QQuickShape *>(property->object);
1375 QQuickShapePrivate *d = QQuickShapePrivate::get(item);
1376 QQuickShapePath *path = qobject_cast<QQuickShapePath *>(obj);
1377 if (path) {
1378 QQuickShapePathPrivate::get(path)->dirty = QQuickShapePathPrivate::DirtyAll;
1379 d->sp.append(path);
1380 }
1381
1382 QQuickItemPrivate::data_append(property, obj);
1383
1384 if (path && d->componentComplete) {
1385 QObject::connect(path, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged()));
1386 d->_q_shapePathChanged();
1387 }
1388}
1389
1390static void vpe_clear(QQmlListProperty<QObject> *property)
1391{
1392 QQuickShape *item = static_cast<QQuickShape *>(property->object);
1393 QQuickShapePrivate *d = QQuickShapePrivate::get(item);
1394
1395 for (QQuickShapePath *p : d->sp)
1396 QObject::disconnect(p, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged()));
1397
1398 d->sp.clear();
1399
1400 QQuickItemPrivate::data_clear(property);
1401
1402 if (d->componentComplete)
1403 d->_q_shapePathChanged();
1404}
1405
1406/*!
1407 \qmlproperty list<Object> QtQuick.Shapes::Shape::data
1408
1409 This property holds the ShapePath objects that define the contents of the
1410 Shape. It can also contain any other type of objects, since Shape, like
1411 Item, allows adding any visual or non-visual objects as children.
1412
1413 \qmldefault
1414 */
1415
1416QQmlListProperty<QObject> QQuickShape::data()
1417{
1418 return QQmlListProperty<QObject>(this,
1419 nullptr,
1420 vpe_append,
1421 QQuickItemPrivate::data_count,
1422 QQuickItemPrivate::data_at,
1423 vpe_clear);
1424}
1425
1426void QQuickShape::classBegin()
1427{
1428 QQuickItem::classBegin();
1429}
1430
1431void QQuickShape::componentComplete()
1432{
1433 Q_D(QQuickShape);
1434
1435 QQuickItem::componentComplete();
1436
1437 for (QQuickShapePath *p : d->sp)
1438 connect(p, SIGNAL(shapePathChanged()), this, SLOT(_q_shapePathChanged()));
1439
1440 d->_q_shapePathChanged();
1441}
1442
1443void QQuickShape::updatePolish()
1444{
1445 Q_D(QQuickShape);
1446
1447 const int currentEffectRefCount = d->extra.isAllocated() ? d->extra->recursiveEffectRefCount : 0;
1448 if (!d->spChanged && currentEffectRefCount <= d->effectRefCount)
1449 return;
1450
1451 d->spChanged = false;
1452 d->effectRefCount = currentEffectRefCount;
1453
1454 QQuickShape::RendererType expectedRenderer = d->selectRendererType();
1455 if (d->rendererType != expectedRenderer) {
1456 delete d->renderer;
1457 d->renderer = nullptr;
1458 }
1459
1460 if (!d->renderer) {
1461 d->createRenderer();
1462 if (!d->renderer)
1463 return;
1464 emit rendererChanged();
1465 }
1466
1467 // endSync() is where expensive calculations may happen (or get kicked off
1468 // on worker threads), depending on the backend. Therefore do this only
1469 // when the item is visible.
1470 if (isVisible() || d->effectRefCount > 0)
1471 d->sync();
1472}
1473
1474void QQuickShape::itemChange(ItemChange change, const ItemChangeData &data)
1475{
1476 Q_D(QQuickShape);
1477
1478 // sync may have been deferred; do it now if the item became visible
1479 if (change == ItemVisibleHasChanged && data.boolValue)
1480 d->_q_shapePathChanged();
1481 else if (change == QQuickItem::ItemSceneChange) {
1482 for (int i = 0; i < d->sp.size(); ++i)
1483 QQuickShapePathPrivate::get(d->sp[i])->dirty = QQuickShapePathPrivate::DirtyAll;
1484 d->_q_shapePathChanged();
1485 d->handleSceneChange(data.window);
1486 } else if (change == ItemTransformHasChanged && d->rendererType == QQuickShape::GeometryRenderer) {
1487 bool cosmeticStrokeFound = false;
1488 for (int i = 0; i < d->sp.size(); ++i) {
1489 if (d->sp[i]->cosmeticStroke()) {
1490 QQuickShapePathPrivate::get(d->sp[i])->dirty = QQuickShapePathPrivate::DirtyStrokeWidth;
1491 cosmeticStrokeFound = true;
1492 }
1493 }
1494 if (cosmeticStrokeFound)
1495 d->_q_shapePathChanged();
1496 }
1497
1498 QQuickItem::itemChange(change, data);
1499}
1500
1501QSGNode *QQuickShape::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
1502{
1503 // Called on the render thread, with the gui thread blocked. We can now
1504 // safely access gui thread data.
1505 Q_D(QQuickShape);
1506
1507 if (d->renderer || d->rendererChanged) {
1508 if (!node || d->rendererChanged) {
1509 d->rendererChanged = false;
1510 delete node;
1511 node = d->createNode();
1512 }
1513 if (d->renderer)
1514 d->renderer->updateNode();
1515
1516 // TODO: only add transform node when needed (and then make sure static_cast is safe)
1517 QMatrix4x4 fillModeTransform;
1518 qreal xScale = 1.0;
1519 qreal yScale = 1.0;
1520
1521 if (d->fillMode != NoResize) {
1522 xScale = width() / implicitWidth();
1523 yScale = height() / implicitHeight();
1524
1525 if (d->fillMode == PreserveAspectFit)
1526 xScale = yScale = qMin(xScale, yScale);
1527 else if (d->fillMode == PreserveAspectCrop)
1528 xScale = yScale = qMax(xScale, yScale);
1529 fillModeTransform.scale(xScale, yScale);
1530 }
1531 if (d->horizontalAlignment != AlignLeft || d->verticalAlignment != AlignTop) {
1532 qreal tx = 0;
1533 qreal ty = 0;
1534 qreal w = xScale * implicitWidth();
1535 qreal h = yScale * implicitHeight();
1536 if (d->horizontalAlignment == AlignRight)
1537 tx = width() - w;
1538 else if (d->horizontalAlignment == AlignHCenter)
1539 tx = (width() - w) / 2;
1540 if (d->verticalAlignment == AlignBottom)
1541 ty = height() - h;
1542 else if (d->verticalAlignment == AlignVCenter)
1543 ty = (height() - h) / 2;
1544 fillModeTransform.translate(tx / xScale, ty / yScale);
1545 }
1546
1547 QSGTransformNode *transformNode = static_cast<QSGTransformNode *>(node);
1548 if (fillModeTransform != transformNode->matrix())
1549 transformNode->setMatrix(fillModeTransform);
1550 }
1551 return node;
1552}
1553
1554QQuickShape::RendererType QQuickShapePrivate::selectRendererType()
1555{
1556 QQuickShape::RendererType res = QQuickShape::UnknownRenderer;
1557 Q_Q(QQuickShape);
1558 QSGRendererInterface *ri = q->window()->rendererInterface();
1559 if (!ri)
1560 return res;
1561
1562 static const bool environmentPreferCurve =
1563 qEnvironmentVariable("QT_QUICKSHAPES_BACKEND").toLower() == QLatin1String("curverenderer");
1564
1565 switch (ri->graphicsApi()) {
1566 case QSGRendererInterface::Software:
1567 res = QQuickShape::SoftwareRenderer;
1568 break;
1569 default:
1570 if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
1571 if (preferredType == QQuickShape::CurveRenderer || environmentPreferCurve) {
1572 res = QQuickShape::CurveRenderer;
1573 } else {
1574 res = QQuickShape::GeometryRenderer;
1575 }
1576 } else {
1577 qWarning("No path backend for this graphics API yet");
1578 }
1579 break;
1580 }
1581
1582 return res;
1583}
1584
1585// the renderer object lives on the gui thread
1586void QQuickShapePrivate::createRenderer()
1587{
1588 Q_Q(QQuickShape);
1589 QQuickShape::RendererType selectedType = selectRendererType();
1590 if (selectedType == QQuickShape::UnknownRenderer)
1591 return;
1592
1593 rendererType = selectedType;
1594 rendererChanged = true;
1595
1596 // If cosmetic stroking is used with GeometryRenderer, we need to be notified when the transform changes
1597 q->setFlag(QQuickItem::ItemObservesViewport, rendererType == QQuickShape::GeometryRenderer);
1598
1599 switch (selectedType) {
1600 case QQuickShape::SoftwareRenderer:
1601 renderer = new QQuickShapeSoftwareRenderer;
1602 break;
1603 case QQuickShape::GeometryRenderer:
1604 renderer = new QQuickShapeGenericRenderer(q);
1605 break;
1606 case QQuickShape::CurveRenderer:
1607 renderer = new QQuickShapeCurveRenderer(q);
1608 break;
1609 default:
1610 Q_UNREACHABLE();
1611 break;
1612 }
1613}
1614
1615// the node lives on the render thread
1616QSGNode *QQuickShapePrivate::createNode()
1617{
1618 Q_Q(QQuickShape);
1619 QSGNode *node = nullptr;
1620 if (!q->window() || !renderer)
1621 return node;
1622 QSGRendererInterface *ri = q->window()->rendererInterface();
1623 if (!ri)
1624 return node;
1625
1626 QSGNode *pathNode = nullptr;
1627 switch (ri->graphicsApi()) {
1628 case QSGRendererInterface::Software:
1629 pathNode = new QQuickShapeSoftwareRenderNode(q);
1630 static_cast<QQuickShapeSoftwareRenderer *>(renderer)->setNode(
1631 static_cast<QQuickShapeSoftwareRenderNode *>(pathNode));
1632 break;
1633 default:
1634 if (QSGRendererInterface::isApiRhiBased(ri->graphicsApi())) {
1635 if (rendererType == QQuickShape::CurveRenderer) {
1636 pathNode = new QSGNode;
1637 static_cast<QQuickShapeCurveRenderer *>(renderer)->setRootNode(pathNode);
1638 } else {
1639 pathNode = new QQuickShapeGenericNode;
1640 static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
1641 static_cast<QQuickShapeGenericNode *>(pathNode));
1642 }
1643 } else {
1644 qWarning("No path backend for this graphics API yet");
1645 }
1646 break;
1647 }
1648
1649 // TODO: only create transform node when needed
1650 node = new QSGTransformNode;
1651 node->appendChildNode(pathNode);
1652
1653 return node;
1654}
1655
1656void QQuickShapePrivate::asyncShapeReady(void *data)
1657{
1658 QQuickShapePrivate *self = static_cast<QQuickShapePrivate *>(data);
1659 self->setStatus(QQuickShape::Ready);
1660 if (self->syncTimingActive)
1661 qDebug("[Shape %p] [%d] [dirty=0x%x] async update took %lld ms",
1662 self->q_func(), self->syncTimeCounter, self->syncTimingTotalDirty, self->syncTimer.elapsed());
1663}
1664
1665void QQuickShapePrivate::sync()
1666{
1667 int totalDirty = 0;
1668 syncTimingActive = QQSHAPE_LOG_TIME_DIRTY_SYNC().isDebugEnabled();
1669 if (syncTimingActive)
1670 syncTimer.start();
1671
1672 const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync);
1673 if (useAsync) {
1674 setStatus(QQuickShape::Processing);
1675 renderer->setAsyncCallback(asyncShapeReady, this);
1676 }
1677
1678 const int count = sp.size();
1679 bool countChanged = false;
1680 const qreal det = windowToItemTransform().determinant();
1681 const qreal adjTriangulationScale = triangulationScale /
1682 (qIsNaN(det) || qIsNull(det) ? qreal(1) : qSqrt(qAbs(det)));
1683 renderer->beginSync(count, &countChanged);
1684
1685 qCDebug(lcShapeSync) << "syncing" << count << "path(s)";
1686 for (int i = 0; i < count; ++i) {
1687 QQuickShapePath *p = sp[i];
1688 qCDebug(lcShapeSync) << "- syncing path:" << p;
1689 int &dirty(QQuickShapePathPrivate::get(p)->dirty);
1690 totalDirty |= dirty;
1691
1692 if (dirty & (QQuickShapePathPrivate::DirtyPath | QQuickShapePathPrivate::DirtyTrim)) {
1693 qCDebug(lcShapeSync) << " - DirtyPath";
1694 renderer->setPath(i, p);
1695 }
1696 if (dirty & QQuickShapePathPrivate::DirtyStrokeColor) {
1697 qCDebug(lcShapeSync) << " - DirtyStrokeColor:" << p->strokeColor();
1698 renderer->setStrokeColor(i, p->strokeColor());
1699 }
1700 if (dirty & QQuickShapePathPrivate::DirtyStrokeWidth) {
1701 // TODO adjust triangulationScale regardless of the env var, after we're satisfied that there are no significant regressions
1702 if (p->cosmeticStroke() || QSGCurveStrokeNode::expandingStrokeEnabled()) {
1703 renderer->setTriangulationScale(i, adjTriangulationScale);
1704 qCDebug(lcShapeSync) << " - DirtyStrokeWidth:" << p->strokeWidth()
1705 << "cosmetic:" << p->cosmeticStroke() << "triangulationScale"
1706 << triangulationScale << "adjusted to" << adjTriangulationScale;
1707 } else {
1708 renderer->setTriangulationScale(i, triangulationScale);
1709 qCDebug(lcShapeSync) << " - DirtyStrokeWidth:" << p->strokeWidth()
1710 << "cosmetic:" << p->cosmeticStroke() << "triangulationScale" << triangulationScale;
1711 }
1712 renderer->setStrokeWidth(i, p->strokeWidth());
1713 renderer->setCosmeticStroke(i, p->cosmeticStroke());
1714 }
1715 if (dirty & QQuickShapePathPrivate::DirtyFillColor)
1716 renderer->setFillColor(i, p->fillColor());
1717 if (dirty & QQuickShapePathPrivate::DirtyFillRule)
1718 renderer->setFillRule(i, p->fillRule());
1719 if (dirty & QQuickShapePathPrivate::DirtyStyle) {
1720 renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit());
1721 renderer->setCapStyle(i, p->capStyle());
1722 }
1723 if (dirty & QQuickShapePathPrivate::DirtyDash)
1724 renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern());
1725 if (dirty & QQuickShapePathPrivate::DirtyFillGradient)
1726 renderer->setFillGradient(i, p->fillGradient());
1727 if (dirty & QQuickShapePathPrivate::DirtyStrokeGradient)
1728 renderer->setStrokeGradient(i, p->strokeGradient());
1729 if (dirty & QQuickShapePathPrivate::DirtyFillTransform)
1730 renderer->setFillTransform(i, QQuickShapePathPrivate::get(p)->sfp.fillTransform);
1731 if (dirty & QQuickShapePathPrivate::DirtyVisible)
1732 renderer->setVisible(i, p->isVisible());
1733 if (dirty & QQuickShapePathPrivate::DirtyFillItem) {
1734 if (p->fillItem() == nullptr) {
1735 renderer->setFillTextureProvider(i, nullptr);
1736 } else if (p->fillItem()->isTextureProvider()) {
1737 renderer->setFillTextureProvider(i, p->fillItem());
1738 } else {
1739 renderer->setFillTextureProvider(i, nullptr);
1740 qWarning() << "QQuickShape: Fill item is not texture provider";
1741 }
1742 }
1743
1744 dirty = 0;
1745 }
1746
1747 syncTimingTotalDirty = totalDirty;
1748 if (syncTimingTotalDirty)
1749 ++syncTimeCounter;
1750 else
1751 syncTimingActive = false;
1752
1753 renderer->endSync(useAsync);
1754
1755 if (!useAsync) {
1756 setStatus(QQuickShape::Ready);
1757 if (syncTimingActive)
1758 qDebug("[Shape %p] [%d] [dirty=0x%x] update took %lld ms",
1759 q_func(), syncTimeCounter, syncTimingTotalDirty, syncTimer.elapsed());
1760 }
1761
1762 // Must dirty the QQuickItem if something got changed, nothing
1763 // else does this for us.
1764 Q_Q(QQuickShape);
1765 if (totalDirty || countChanged)
1766 q->update();
1767}
1768
1769// ***** gradient support *****
1770
1771/*!
1772 \qmltype ShapeGradient
1773 //! \nativetype QQuickShapeGradient
1774 \inqmlmodule QtQuick.Shapes
1775 \ingroup qtquick-paths
1776 \ingroup qtquick-views
1777 \inherits Gradient
1778 \brief Base type of Shape fill gradients.
1779 \since 5.10
1780
1781 This is an abstract base class for gradients like LinearGradient and
1782 cannot be created directly. It extends \l Gradient with properties like the
1783 spread mode.
1784 */
1785
1786QQuickShapeGradient::QQuickShapeGradient(QObject *parent)
1787 : QQuickGradient(parent),
1788 m_spread(PadSpread)
1789{
1790}
1791
1792/*!
1793 \qmlproperty enumeration QtQuick.Shapes::ShapeGradient::spread
1794
1795 Specifies how the area outside the gradient area should be filled. The
1796 default value is \c ShapeGradient.PadSpread.
1797
1798 \value ShapeGradient.PadSpread
1799 The area is filled with the closest stop color.
1800
1801 \value ShapeGradient.RepeatSpread
1802 The gradient is repeated outside the gradient area.
1803
1804 \value ShapeGradient.ReflectSpread
1805 The gradient is reflected outside the gradient area.
1806 */
1807
1808QQuickShapeGradient::SpreadMode QQuickShapeGradient::spread() const
1809{
1810 return m_spread;
1811}
1812
1813void QQuickShapeGradient::setSpread(SpreadMode mode)
1814{
1815 if (m_spread != mode) {
1816 m_spread = mode;
1817 emit spreadChanged();
1818 emit updated();
1819 }
1820}
1821
1822/*!
1823 \qmltype LinearGradient
1824 //! \nativetype QQuickShapeLinearGradient
1825 \inqmlmodule QtQuick.Shapes
1826 \ingroup qtquick-paths
1827 \ingroup qtquick-views
1828 \inherits ShapeGradient
1829 \brief Linear gradient.
1830 \since 5.10
1831
1832 Linear gradients interpolate colors between start and end points in Shape
1833 items. Outside these points the gradient is either padded, reflected or
1834 repeated depending on the spread type.
1835
1836 \note LinearGradient is only supported in combination with Shape items. It
1837 is not compatible with \l Rectangle, as that only supports \l Gradient.
1838
1839 \sa QLinearGradient
1840 */
1841
1842QQuickShapeLinearGradient::QQuickShapeLinearGradient(QObject *parent)
1843 : QQuickShapeGradient(parent)
1844{
1845}
1846
1847/*!
1848 \qmlproperty real QtQuick.Shapes::LinearGradient::x1
1849 \qmlproperty real QtQuick.Shapes::LinearGradient::y1
1850 \qmlproperty real QtQuick.Shapes::LinearGradient::x2
1851 \qmlproperty real QtQuick.Shapes::LinearGradient::y2
1852
1853 These properties define the start and end points between which color
1854 interpolation occurs. By default both points are set to (0, 0).
1855 */
1856
1857qreal QQuickShapeLinearGradient::x1() const
1858{
1859 return m_start.x();
1860}
1861
1862void QQuickShapeLinearGradient::setX1(qreal v)
1863{
1864 if (m_start.x() != v) {
1865 m_start.setX(v);
1866 emit x1Changed();
1867 emit updated();
1868 }
1869}
1870
1871qreal QQuickShapeLinearGradient::y1() const
1872{
1873 return m_start.y();
1874}
1875
1876void QQuickShapeLinearGradient::setY1(qreal v)
1877{
1878 if (m_start.y() != v) {
1879 m_start.setY(v);
1880 emit y1Changed();
1881 emit updated();
1882 }
1883}
1884
1885qreal QQuickShapeLinearGradient::x2() const
1886{
1887 return m_end.x();
1888}
1889
1890void QQuickShapeLinearGradient::setX2(qreal v)
1891{
1892 if (m_end.x() != v) {
1893 m_end.setX(v);
1894 emit x2Changed();
1895 emit updated();
1896 }
1897}
1898
1899qreal QQuickShapeLinearGradient::y2() const
1900{
1901 return m_end.y();
1902}
1903
1904void QQuickShapeLinearGradient::setY2(qreal v)
1905{
1906 if (m_end.y() != v) {
1907 m_end.setY(v);
1908 emit y2Changed();
1909 emit updated();
1910 }
1911}
1912
1913/*!
1914 \qmltype RadialGradient
1915 //! \nativetype QQuickShapeRadialGradient
1916 \inqmlmodule QtQuick.Shapes
1917 \ingroup qtquick-paths
1918 \ingroup qtquick-views
1919 \inherits ShapeGradient
1920 \brief Radial gradient.
1921 \since 5.10
1922
1923 Radial gradients interpolate colors between a focal circle and a center
1924 circle in Shape items. Points outside the cone defined by the two circles
1925 will be transparent.
1926
1927 Outside the end points the gradient is either padded, reflected or repeated
1928 depending on the spread type.
1929
1930 Below is an example of a simple radial gradient. Here the colors are
1931 interpolated between the specified point and the end points on a circle
1932 specified by the radius:
1933
1934 \code
1935 fillGradient: RadialGradient {
1936 centerX: 50; centerY: 50
1937 centerRadius: 100
1938 focalX: centerX; focalY: centerY
1939 GradientStop { position: 0; color: "blue" }
1940 GradientStop { position: 0.2; color: "green" }
1941 GradientStop { position: 0.4; color: "red" }
1942 GradientStop { position: 0.6; color: "yellow" }
1943 GradientStop { position: 1; color: "cyan" }
1944 }
1945 \endcode
1946
1947 \image shape-radial-gradient.png
1948 {Ellipse with radial gradient from white center to cyan edge}
1949
1950 Extended radial gradients, where a separate focal circle is specified, are
1951 also supported.
1952
1953 \note RadialGradient is only supported in combination with Shape items. It
1954 is not compatible with \l Rectangle, as that only supports \l Gradient.
1955
1956 \sa QRadialGradient
1957 */
1958
1959QQuickShapeRadialGradient::QQuickShapeRadialGradient(QObject *parent)
1960 : QQuickShapeGradient(parent)
1961{
1962}
1963
1964/*!
1965 \qmlproperty real QtQuick.Shapes::RadialGradient::centerX
1966 \qmlproperty real QtQuick.Shapes::RadialGradient::centerY
1967 \qmlproperty real QtQuick.Shapes::RadialGradient::focalX
1968 \qmlproperty real QtQuick.Shapes::RadialGradient::focalY
1969
1970 These properties define the center and focal points. To specify a simple
1971 radial gradient, set focalX and focalY to the value of centerX and
1972 centerY, respectively.
1973 */
1974
1975qreal QQuickShapeRadialGradient::centerX() const
1976{
1977 return m_centerPoint.x();
1978}
1979
1980void QQuickShapeRadialGradient::setCenterX(qreal v)
1981{
1982 if (m_centerPoint.x() != v) {
1983 m_centerPoint.setX(v);
1984 emit centerXChanged();
1985 emit updated();
1986 }
1987}
1988
1989qreal QQuickShapeRadialGradient::centerY() const
1990{
1991 return m_centerPoint.y();
1992}
1993
1994void QQuickShapeRadialGradient::setCenterY(qreal v)
1995{
1996 if (m_centerPoint.y() != v) {
1997 m_centerPoint.setY(v);
1998 emit centerYChanged();
1999 emit updated();
2000 }
2001}
2002
2003/*!
2004 \qmlproperty real QtQuick.Shapes::RadialGradient::centerRadius
2005 \qmlproperty real QtQuick.Shapes::RadialGradient::focalRadius
2006
2007 These properties define the center and focal radius. For simple radial
2008 gradients, focalRadius should be set to \c 0 (the default value).
2009 */
2010
2011qreal QQuickShapeRadialGradient::centerRadius() const
2012{
2013 return m_centerRadius;
2014}
2015
2016void QQuickShapeRadialGradient::setCenterRadius(qreal v)
2017{
2018 if (m_centerRadius != v) {
2019 m_centerRadius = v;
2020 emit centerRadiusChanged();
2021 emit updated();
2022 }
2023}
2024
2025qreal QQuickShapeRadialGradient::focalX() const
2026{
2027 return m_focalPoint.x();
2028}
2029
2030void QQuickShapeRadialGradient::setFocalX(qreal v)
2031{
2032 if (m_focalPoint.x() != v) {
2033 m_focalPoint.setX(v);
2034 emit focalXChanged();
2035 emit updated();
2036 }
2037}
2038
2039qreal QQuickShapeRadialGradient::focalY() const
2040{
2041 return m_focalPoint.y();
2042}
2043
2044void QQuickShapeRadialGradient::setFocalY(qreal v)
2045{
2046 if (m_focalPoint.y() != v) {
2047 m_focalPoint.setY(v);
2048 emit focalYChanged();
2049 emit updated();
2050 }
2051}
2052
2053qreal QQuickShapeRadialGradient::focalRadius() const
2054{
2055 return m_focalRadius;
2056}
2057
2058void QQuickShapeRadialGradient::setFocalRadius(qreal v)
2059{
2060 if (m_focalRadius != v) {
2061 m_focalRadius = v;
2062 emit focalRadiusChanged();
2063 emit updated();
2064 }
2065}
2066
2067/*!
2068 \qmltype ConicalGradient
2069 //! \nativetype QQuickShapeConicalGradient
2070 \inqmlmodule QtQuick.Shapes
2071 \ingroup qtquick-paths
2072 \ingroup qtquick-views
2073 \inherits ShapeGradient
2074 \brief Conical gradient.
2075 \since 5.10
2076
2077 Conical gradients interpolate colors counter-clockwise around a center
2078 point in Shape items.
2079
2080 \note The \l{ShapeGradient::spread}{spread mode} setting has no effect for
2081 conical gradients.
2082
2083 \note ConicalGradient is only supported in combination with Shape items. It
2084 is not compatible with \l Rectangle, as that only supports \l Gradient.
2085
2086 \sa QConicalGradient
2087 */
2088
2089QQuickShapeConicalGradient::QQuickShapeConicalGradient(QObject *parent)
2090 : QQuickShapeGradient(parent)
2091{
2092}
2093
2094/*!
2095 \qmlproperty real QtQuick.Shapes::ConicalGradient::centerX
2096 \qmlproperty real QtQuick.Shapes::ConicalGradient::centerY
2097
2098 These properties define the center point of the conical gradient.
2099 */
2100
2101qreal QQuickShapeConicalGradient::centerX() const
2102{
2103 return m_centerPoint.x();
2104}
2105
2106void QQuickShapeConicalGradient::setCenterX(qreal v)
2107{
2108 if (m_centerPoint.x() != v) {
2109 m_centerPoint.setX(v);
2110 emit centerXChanged();
2111 emit updated();
2112 }
2113}
2114
2115qreal QQuickShapeConicalGradient::centerY() const
2116{
2117 return m_centerPoint.y();
2118}
2119
2120void QQuickShapeConicalGradient::setCenterY(qreal v)
2121{
2122 if (m_centerPoint.y() != v) {
2123 m_centerPoint.setY(v);
2124 emit centerYChanged();
2125 emit updated();
2126 }
2127}
2128
2129/*!
2130 \qmlproperty real QtQuick.Shapes::ConicalGradient::angle
2131
2132 This property defines the start angle for the conical gradient. The value
2133 is in degrees (0-360).
2134 */
2135
2136qreal QQuickShapeConicalGradient::angle() const
2137{
2138 return m_angle;
2139}
2140
2141void QQuickShapeConicalGradient::setAngle(qreal v)
2142{
2143 if (m_angle != v) {
2144 m_angle = v;
2145 emit angleChanged();
2146 emit updated();
2147 }
2148}
2149
2150QT_END_NAMESPACE
2151
2152#include "moc_qquickshape_p.cpp"
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
static void initResources()
static void vpe_clear(QQmlListProperty< QObject > *property)
static void vpe_append(QQmlListProperty< QObject > *property, QObject *obj)
Q_GHS_KEEP_REFERENCE(QQuickShapes_initializeModule)