diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2017-09-22 22:48:25 +0200 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2018-01-25 16:23:08 +0000 |
commit | bf5f171db212b7e57493f31f41e05e2d68ade791 (patch) | |
tree | 5b9b1c0796c5910e8a1cb918fd685bffc0a2d8ec /src/imports/shapes | |
parent | bf74a908cb0591c2adc024a6f93d566c7348c125 (diff) |
QQuickShape: override contains(QPointF); demonstrate in examples
QQuickItem::contains() only checks the Item's bounding box by default.
In the case of Shapes, that can be comically imprecise, but it's fast.
So we add a containsMode property to control whether we will do that
(the default) or actually check each of the QPainterPaths within to
see whether they contain the point (FillContains).
FillContains could be optimized later: use QRegion perhaps, or download
the rendered texture from the GPU and test whether the pixel at the
point is transparent. It may also be appropriate to add a StrokeContains
option.
The main motivation is to detect mouse (or touch) interaction within
a shaped area. QQuickSinglePointHandler::wantsEventPoint() already
checks whether its parent Item contains the event point. So if a
Shape has a TapHandler for example, it will respond only within the
visible bounds of the Shape rather than within the entire rectangular
bounding box as long as containsMode is set to FillContains.
Examples quick/shapes/content/tapableTriangle.qml and tiger.qml
are modified to react when a press occurs inside, and the former
is fixed to be able to run standalone via the qml runtime. The
latter has an offset issue when run standalone but is OK within
the shape gallery example.
As a drive-by optimization, QQuickShapePrivate's variables are
re-ordered by type so that the compiler can place the bools and
enums into bitfields; and to facilitate reordering, the
initialization is done C++11-style, in the header.
[ChangeLog][QtQuick][Shape] A containsMode property is added.
If it is set to FillContains, then Shape.contains() returns true
only within the visible bounds, so its Pointer Handlers also respond
only within those bounds.
Change-Id: I31c85a9b08aa6945c58dc07febfe89ffef21274b
Reviewed-by: Paolo Angelelli <paolo.angelelli@qt.io>
Diffstat (limited to 'src/imports/shapes')
-rw-r--r-- | src/imports/shapes/plugin.cpp | 6 | ||||
-rw-r--r-- | src/imports/shapes/plugins.qmltypes | 15 | ||||
-rw-r--r-- | src/imports/shapes/qquickshape.cpp | 61 | ||||
-rw-r--r-- | src/imports/shapes/qquickshape_p.h | 13 | ||||
-rw-r--r-- | src/imports/shapes/qquickshape_p_p.h | 1 | ||||
-rw-r--r-- | src/imports/shapes/shapes.pro | 2 |
6 files changed, 92 insertions, 6 deletions
diff --git a/src/imports/shapes/plugin.cpp b/src/imports/shapes/plugin.cpp index e24826ee55..74731aa35f 100644 --- a/src/imports/shapes/plugin.cpp +++ b/src/imports/shapes/plugin.cpp @@ -68,6 +68,12 @@ public: qmlRegisterType<QQuickShapeLinearGradient>(uri, 1, 0, "LinearGradient"); qmlRegisterType<QQuickShapeRadialGradient>(uri, 1, 0, "RadialGradient"); qmlRegisterType<QQuickShapeConicalGradient>(uri, 1, 0, "ConicalGradient"); + + // Auto-increment the import to stay in sync with ALL future QtQuick minor versions + qmlRegisterModule(uri, 1, QT_VERSION_MINOR); + + // revision in Qt 5.11: added containsMode property + qmlRegisterType<QQuickShape, 11>(uri, 1, 11, "Shape"); } }; diff --git a/src/imports/shapes/plugins.qmltypes b/src/imports/shapes/plugins.qmltypes index 39aab42401..a140644d6d 100644 --- a/src/imports/shapes/plugins.qmltypes +++ b/src/imports/shapes/plugins.qmltypes @@ -4,7 +4,7 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable QtQuick.Shapes 1.0' +// 'qmlplugindump -nonrelocatable QtQuick.Shapes 1.11' Module { dependencies: ["QtQuick 2.8"] @@ -12,8 +12,8 @@ Module { name: "QQuickShape" defaultProperty: "data" prototype: "QQuickItem" - exports: ["QtQuick.Shapes/Shape 1.0"] - exportMetaObjectRevisions: [0] + exports: ["QtQuick.Shapes/Shape 1.0", "QtQuick.Shapes/Shape 1.11"] + exportMetaObjectRevisions: [0, 11] Enum { name: "RendererType" values: { @@ -31,12 +31,21 @@ Module { "Processing": 2 } } + Enum { + name: "ContainsMode" + values: { + "BoundingRectContains": 0, + "FillContains": 1 + } + } Property { name: "rendererType"; type: "RendererType"; isReadonly: true } Property { name: "asynchronous"; type: "bool" } Property { name: "vendorExtensionsEnabled"; type: "bool" } Property { name: "status"; type: "Status"; isReadonly: true } + Property { name: "containsMode"; revision: 11; type: "ContainsMode" } Property { name: "data"; type: "QObject"; isList: true; isReadonly: true } Signal { name: "rendererChanged" } + Signal { name: "containsModeChanged"; revision: 11 } } Component { name: "QQuickShapeConicalGradient" diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index 518f7a08cf..54a0a3d402 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(QQSHAPE_LOG_TIME_DIRTY_SYNC, "qt.shape.time.sync") /*! - \qmlmodule QtQuick.Shapes 1.0 + \qmlmodule QtQuick.Shapes 1.11 \title Qt Quick Shapes QML Types \ingroup qmlmodules \brief Provides QML types for drawing stroked and filled shapes. @@ -61,7 +61,7 @@ Q_LOGGING_CATEGORY(QQSHAPE_LOG_TIME_DIRTY_SYNC, "qt.shape.time.sync") To use the types in this module, import the module with the following line: \badcode - import QtQuick.Shapes 1.0 + import QtQuick.Shapes 1.11 \endcode */ @@ -778,6 +778,63 @@ QQuickShape::Status QQuickShape::status() const return d->status; } +/*! + \qmlproperty enumeration QtQuick.Shapes::Shape::containsMode + \since QtQuick.Shapes 1.11 + + This property determines the definition of \l {QQuickItem::contains()}{contains()} + for the Shape. It is useful in case you add + \l {Qt Quick Pointer Handlers QML Types}{Pointer Handlers} and you + want to react only when the mouse or touchpoint is fully inside the Shape. + + \value Shape.BoundingRectContains + The default implementation of \l QQuickItem::contains() checks only + whether the given point is inside the rectangular bounding box. This is + the most efficient implementation, which is why it's the default. + + \value Shape.FillContains + Check whether the interior (the part that would be filled if you are + rendering it with fill) of any \l ShapePath that makes up this Shape + contains the given point. The more complex and numerous ShapePaths you + add, the less efficient this is to check, which can potentially slow + down event delivery in your application. So it should be used with care. + + One way to speed up the \c FillContains check is to generate an approximate + outline with as few points as possible, place that in a transparent Shape + on top, and add your Pointer Handlers to that, so that the containment + check is cheaper during event delivery. +*/ +QQuickShape::ContainsMode QQuickShape::containsMode() const +{ + Q_D(const QQuickShape); + return d->containsMode; +} + +void QQuickShape::setContainsMode(QQuickShape::ContainsMode containsMode) +{ + Q_D(QQuickShape); + if (d->containsMode == containsMode) + return; + + d->containsMode = containsMode; + emit containsModeChanged(); +} + +bool QQuickShape::contains(const QPointF &point) const +{ + Q_D(const QQuickShape); + switch (d->containsMode) { + case BoundingRectContains: + return QQuickItem::contains(point); + case FillContains: + for (QQuickShapePath *path : d->sp) { + if (path->path().contains(point)) + return true; + } + } + return false; +} + static void vpe_append(QQmlListProperty<QObject> *property, QObject *obj) { QQuickShape *item = static_cast<QQuickShape *>(property->object); diff --git a/src/imports/shapes/qquickshape_p.h b/src/imports/shapes/qquickshape_p.h index 365d9644c7..1dfeaf9228 100644 --- a/src/imports/shapes/qquickshape_p.h +++ b/src/imports/shapes/qquickshape_p.h @@ -303,6 +303,7 @@ class QQuickShape : public QQuickItem Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) Q_PROPERTY(bool vendorExtensionsEnabled READ vendorExtensionsEnabled WRITE setVendorExtensionsEnabled NOTIFY vendorExtensionsEnabledChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(ContainsMode containsMode READ containsMode WRITE setContainsMode NOTIFY containsModeChanged REVISION 11) Q_PROPERTY(QQmlListProperty<QObject> data READ data) Q_CLASSINFO("DefaultProperty", "data") @@ -322,6 +323,12 @@ public: }; Q_ENUM(Status) + enum ContainsMode { + BoundingRectContains, + FillContains + }; + Q_ENUM(ContainsMode) + QQuickShape(QQuickItem *parent = nullptr); ~QQuickShape(); @@ -335,6 +342,11 @@ public: Status status() const; + ContainsMode containsMode() const; + void setContainsMode(ContainsMode containsMode); + + bool contains(const QPointF &point) const override; + QQmlListProperty<QObject> data(); protected: @@ -349,6 +361,7 @@ Q_SIGNALS: void asynchronousChanged(); void vendorExtensionsEnabledChanged(); void statusChanged(); + Q_REVISION(11) void containsModeChanged(); private: Q_DISABLE_COPY(QQuickShape) diff --git a/src/imports/shapes/qquickshape_p_p.h b/src/imports/shapes/qquickshape_p_p.h index eb7fdc3bc8..c3d84b6353 100644 --- a/src/imports/shapes/qquickshape_p_p.h +++ b/src/imports/shapes/qquickshape_p_p.h @@ -177,6 +177,7 @@ public: int syncTimeCounter = 0; QQuickShape::Status status = QQuickShape::Null; QQuickShape::RendererType rendererType = QQuickShape::UnknownRenderer; + QQuickShape::ContainsMode containsMode = QQuickShape::BoundingRectContains; bool spChanged = false; bool async = false; bool enableVendorExts = true; diff --git a/src/imports/shapes/shapes.pro b/src/imports/shapes/shapes.pro index fee950a529..4d6e9508af 100644 --- a/src/imports/shapes/shapes.pro +++ b/src/imports/shapes/shapes.pro @@ -1,7 +1,7 @@ CXX_MODULE = qml TARGET = qmlshapesplugin TARGETPATH = QtQuick/Shapes -IMPORT_VERSION = 1.0 +IMPORT_VERSION = 1.11 QT = core gui-private qml quick-private |