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