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