aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick')
-rw-r--r--src/quick/configure.json12
-rw-r--r--src/quick/designer/qqmldesignermetaobject.cpp4
-rw-r--r--src/quick/doc/images/qml-item-canvas-lineDash.pngbin0 -> 6254 bytes
-rw-r--r--src/quick/doc/snippets/qml/pathview/pathview.qml4
-rw-r--r--src/quick/doc/snippets/qml/rectangle/rectangle-gradient.qml12
-rw-r--r--src/quick/handlers/handlers.pri9
-rw-r--r--src/quick/handlers/qquickdragaxis.cpp75
-rw-r--r--src/quick/handlers/qquickdragaxis_p.h87
-rw-r--r--src/quick/handlers/qquickdraghandler.cpp35
-rw-r--r--src/quick/handlers/qquickdraghandler_p.h31
-rw-r--r--src/quick/handlers/qquickhandlerpoint.cpp351
-rw-r--r--src/quick/handlers/qquickhandlerpoint_p.h120
-rw-r--r--src/quick/handlers/qquickhandlersmodule.cpp2
-rw-r--r--src/quick/handlers/qquickhoverhandler.cpp107
-rw-r--r--src/quick/handlers/qquickhoverhandler_p.h91
-rw-r--r--src/quick/handlers/qquickmultipointhandler.cpp73
-rw-r--r--src/quick/handlers/qquickmultipointhandler_p.h15
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp64
-rw-r--r--src/quick/handlers/qquickpinchhandler_p.h13
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler.cpp103
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler_p.h26
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler_p_p.h75
-rw-r--r--src/quick/handlers/qquickpointerhandler.cpp211
-rw-r--r--src/quick/handlers/qquickpointerhandler_p.h38
-rw-r--r--src/quick/handlers/qquickpointerhandler_p_p.h85
-rw-r--r--src/quick/handlers/qquickpointhandler.cpp2
-rw-r--r--src/quick/handlers/qquicksinglepointhandler.cpp300
-rw-r--r--src/quick/handlers/qquicksinglepointhandler_p.h63
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp6
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp172
-rw-r--r--src/quick/items/context2d/qquickcontext2d_p.h5
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp26
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h14
-rw-r--r--src/quick/items/items.pri10
-rw-r--r--src/quick/items/qquickanimatedsprite.cpp11
-rw-r--r--src/quick/items/qquickanimatedsprite_p.h2
-rw-r--r--src/quick/items/qquickevents.cpp214
-rw-r--r--src/quick/items/qquickevents_p_p.h108
-rw-r--r--src/quick/items/qquickflickable.cpp32
-rw-r--r--src/quick/items/qquickflickable_p.h5
-rw-r--r--src/quick/items/qquickflickable_p_p.h1
-rw-r--r--src/quick/items/qquickgridview.cpp10
-rw-r--r--src/quick/items/qquickitem.cpp26
-rw-r--r--src/quick/items/qquickitem_p.h2
-rw-r--r--src/quick/items/qquickitemsmodule.cpp4
-rw-r--r--src/quick/items/qquickitemview.cpp106
-rw-r--r--src/quick/items/qquickitemview_p_p.h37
-rw-r--r--src/quick/items/qquickitemviewfxitem.cpp165
-rw-r--r--src/quick/items/qquickitemviewfxitem_p_p.h108
-rw-r--r--src/quick/items/qquicklistview.cpp10
-rw-r--r--src/quick/items/qquickloader.cpp17
-rw-r--r--src/quick/items/qquickloader_p_p.h3
-rw-r--r--src/quick/items/qquickrectangle.cpp121
-rw-r--r--src/quick/items/qquickrectangle_p.h17
-rw-r--r--src/quick/items/qquickrectangle_p_p.h2
-rw-r--r--src/quick/items/qquickrendercontrol.cpp1
-rw-r--r--src/quick/items/qquickscalegrid.cpp4
-rw-r--r--src/quick/items/qquickscalegrid_p_p.h12
-rw-r--r--src/quick/items/qquicktableview.cpp1463
-rw-r--r--src/quick/items/qquicktableview_p.h194
-rw-r--r--src/quick/items/qquicktableview_p_p.h342
-rw-r--r--src/quick/items/qquicktext.cpp47
-rw-r--r--src/quick/items/qquicktext_p_p.h1
-rw-r--r--src/quick/items/qquickwindow.cpp201
-rw-r--r--src/quick/items/qquickwindow_p.h9
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp5
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h1
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp4
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp12
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h4
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp4
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer.cpp12
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h10
-rw-r--r--src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp225
-rw-r--r--src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h2
-rw-r--r--src/quick/util/qquickanimation.cpp22
-rw-r--r--src/quick/util/qquickanimation_p.h1
-rw-r--r--src/quick/util/qquickglobal.cpp2
-rw-r--r--src/quick/util/qquickimageprovider.cpp5
-rw-r--r--src/quick/util/qquickimageprovider.h6
-rw-r--r--src/quick/util/qquickimageprovider_p.h72
-rw-r--r--src/quick/util/qquickpixmapcache.cpp19
-rw-r--r--src/quick/util/qquickprofiler.cpp3
-rw-r--r--src/quick/util/qquickprofiler_p.h2
-rw-r--r--src/quick/util/qquickpropertychanges.cpp6
-rw-r--r--src/quick/util/qquickstate.cpp2
-rw-r--r--src/quick/util/qquickstate_p_p.h4
-rw-r--r--src/quick/util/qquicksvgparser.cpp3
-rw-r--r--src/quick/util/qquickutilmodule.cpp3
-rw-r--r--src/quick/util/util.pri1
91 files changed, 4744 insertions, 1204 deletions
diff --git a/src/quick/configure.json b/src/quick/configure.json
index ea1d49baad..ab356be557 100644
--- a/src/quick/configure.json
+++ b/src/quick/configure.json
@@ -15,6 +15,7 @@
"quick-flipable": "boolean",
"quick-gridview": "boolean",
"quick-listview": "boolean",
+ "quick-tableview": "boolean",
"quick-path": "boolean",
"quick-pathview": "boolean",
"quick-positioners": "boolean",
@@ -86,7 +87,7 @@
},
"quick-itemview": {
"label": "ItemView item",
- "condition": "features.quick-gridview || features.quick-listview",
+ "condition": "features.quick-gridview || features.quick-listview || features.quick-tableview",
"output": [
"privateFeature"
]
@@ -107,6 +108,14 @@
"privateFeature"
]
},
+ "quick-tableview": {
+ "label": "TableView item",
+ "purpose": "Provides the TableView item.",
+ "section": "Qt Quick",
+ "output": [
+ "privateFeature"
+ ]
+ },
"quick-particles": {
"label": "Particle support",
"purpose": "Provides a particle system.",
@@ -183,6 +192,7 @@
"quick-flipable",
"quick-gridview",
"quick-listview",
+ "quick-tableview",
"quick-path",
"quick-pathview",
"quick-positioners",
diff --git a/src/quick/designer/qqmldesignermetaobject.cpp b/src/quick/designer/qqmldesignermetaobject.cpp
index 09493c30d6..2efcdada8b 100644
--- a/src/quick/designer/qqmldesignermetaobject.cpp
+++ b/src/quick/designer/qqmldesignermetaobject.cpp
@@ -83,7 +83,7 @@ static QQmlPropertyCache *cacheForObject(QObject *object, QQmlEngine *engine)
{
QQmlVMEMetaObject *metaObject = QQmlVMEMetaObject::get(object);
if (metaObject)
- return metaObject->cache;
+ return metaObject->cache.data();
return QQmlEnginePrivate::get(engine)->cache(object);
}
@@ -139,7 +139,7 @@ QQmlDesignerMetaObject::QQmlDesignerMetaObject(QObject *object, QQmlEngine *engi
cache->setParent(ddata->propertyCache);
cache->invalidate(engine, this);
ddata->propertyCache->release();
- ddata->propertyCache = cache;
+ ddata->propertyCache = cache.data();
ddata->propertyCache->addref();
}
diff --git a/src/quick/doc/images/qml-item-canvas-lineDash.png b/src/quick/doc/images/qml-item-canvas-lineDash.png
new file mode 100644
index 0000000000..a2a038abe0
--- /dev/null
+++ b/src/quick/doc/images/qml-item-canvas-lineDash.png
Binary files differ
diff --git a/src/quick/doc/snippets/qml/pathview/pathview.qml b/src/quick/doc/snippets/qml/pathview/pathview.qml
index 1a31f1372b..58d19b1a0c 100644
--- a/src/quick/doc/snippets/qml/pathview/pathview.qml
+++ b/src/quick/doc/snippets/qml/pathview/pathview.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -59,6 +59,7 @@ Rectangle {
id: delegate
Column {
id: wrapper
+ opacity: PathView.isCurrentItem ? 1 : 0.5
Image {
anchors.horizontalCenter: nameText.horizontalCenter
width: 64; height: 64
@@ -68,7 +69,6 @@ Rectangle {
id: nameText
text: name
font.pointSize: 16
- color: wrapper.PathView.isCurrentItem ? "red" : "black"
}
}
}
diff --git a/src/quick/doc/snippets/qml/rectangle/rectangle-gradient.qml b/src/quick/doc/snippets/qml/rectangle/rectangle-gradient.qml
index 031bee960d..e52a0ce0ef 100644
--- a/src/quick/doc/snippets/qml/rectangle/rectangle-gradient.qml
+++ b/src/quick/doc/snippets/qml/rectangle/rectangle-gradient.qml
@@ -80,5 +80,17 @@ Rectangle {
}
}
//! [rectangles]
+
+//! [presets]
+Rectangle {
+ y: 0; width: 80; height: 80
+ gradient: Gradient.NightFade
+}
+
+Rectangle {
+ y: 0; width: 80; height: 80
+ gradient: "NightFade"
+}
+//! [presets]
}
}
diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri
index 8bd74d95da..111a526ce2 100644
--- a/src/quick/handlers/handlers.pri
+++ b/src/quick/handlers/handlers.pri
@@ -1,17 +1,24 @@
HEADERS += \
$$PWD/qquickdraghandler_p.h \
+ $$PWD/qquickhandlerpoint_p.h \
$$PWD/qquickhandlersmodule_p.h \
+ $$PWD/qquickhoverhandler_p.h \
$$PWD/qquickmultipointhandler_p.h \
$$PWD/qquickpinchhandler_p.h \
$$PWD/qquickpointerdevicehandler_p.h \
+ $$PWD/qquickpointerdevicehandler_p_p.h \
$$PWD/qquickpointerhandler_p.h \
+ $$PWD/qquickpointerhandler_p_p.h \
$$PWD/qquickpointhandler_p.h \
$$PWD/qquicksinglepointhandler_p.h \
$$PWD/qquicktaphandler_p.h \
+ $$PWD/qquickdragaxis_p.h
SOURCES += \
$$PWD/qquickdraghandler.cpp \
+ $$PWD/qquickhandlerpoint.cpp \
$$PWD/qquickhandlersmodule.cpp \
+ $$PWD/qquickhoverhandler.cpp \
$$PWD/qquickmultipointhandler.cpp \
$$PWD/qquickpinchhandler.cpp \
$$PWD/qquickpointerdevicehandler.cpp \
@@ -19,4 +26,4 @@ SOURCES += \
$$PWD/qquickpointhandler.cpp \
$$PWD/qquicksinglepointhandler.cpp \
$$PWD/qquicktaphandler.cpp \
-
+ $$PWD/qquickdragaxis.cpp
diff --git a/src/quick/handlers/qquickdragaxis.cpp b/src/quick/handlers/qquickdragaxis.cpp
new file mode 100644
index 0000000000..5efe19b2fe
--- /dev/null
+++ b/src/quick/handlers/qquickdragaxis.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qquickdragaxis_p.h"
+#include <limits>
+
+QQuickDragAxis::QQuickDragAxis()
+ : m_minimum(-std::numeric_limits<qreal>::max())
+ , m_maximum(std::numeric_limits<qreal>::max())
+ , m_enabled(true)
+{
+}
+
+void QQuickDragAxis::setMinimum(qreal minimum)
+{
+ if (m_minimum == minimum)
+ return;
+
+ m_minimum = minimum;
+ emit minimumChanged();
+}
+
+void QQuickDragAxis::setMaximum(qreal maximum)
+{
+ if (m_maximum == maximum)
+ return;
+
+ m_maximum = maximum;
+ emit maximumChanged();
+}
+
+void QQuickDragAxis::setEnabled(bool enabled)
+{
+ if (m_enabled == enabled)
+ return;
+
+ m_enabled = enabled;
+ emit enabledChanged();
+}
+
diff --git a/src/quick/handlers/qquickdragaxis_p.h b/src/quick/handlers/qquickdragaxis_p.h
new file mode 100644
index 0000000000..2c2e0a426d
--- /dev/null
+++ b/src/quick/handlers/qquickdragaxis_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKDRAGAXIS_P_H
+#define QQUICKDRAGAXIS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qobject.h>
+#include <QtCore/qglobal.h>
+
+class Q_AUTOTEST_EXPORT QQuickDragAxis : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal minimum READ minimum WRITE setMinimum NOTIFY minimumChanged)
+ Q_PROPERTY(qreal maximum READ maximum WRITE setMaximum NOTIFY maximumChanged)
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
+
+public:
+ QQuickDragAxis();
+
+ qreal minimum() const { return m_minimum; }
+ void setMinimum(qreal minimum);
+
+ qreal maximum() const { return m_maximum; }
+ void setMaximum(qreal maximum);
+
+ bool enabled() const { return m_enabled; }
+ void setEnabled(bool enabled);
+
+signals:
+ void minimumChanged();
+ void maximumChanged();
+ void enabledChanged();
+
+private:
+ qreal m_minimum;
+ qreal m_maximum;
+ bool m_enabled;
+};
+
+#endif // QQUICKDRAGAXIS_P_H
diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp
index 5e540b3615..8bb8612fb3 100644
--- a/src/quick/handlers/qquickdraghandler.cpp
+++ b/src/quick/handlers/qquickdraghandler.cpp
@@ -52,7 +52,7 @@ QT_BEGIN_NAMESPACE
\brief Handler for dragging.
DragHandler is a handler that is used to interactively move an Item.
- Like other Pointer Handlers, by default it is fully functional, and
+ Like other Input Handlers, by default it is fully functional, and
manipulates its \l {PointerHandler::target} {target}.
\snippet pointerHandlers/dragHandler.qml 0
@@ -243,39 +243,6 @@ void QQuickDragHandler::setTranslation(const QVector2D &trans)
applied to the \l {PointerHandler::target} {target}.
If \c enabled is true, vertical dragging is allowed.
*/
-QQuickDragAxis::QQuickDragAxis()
- : m_minimum(-DBL_MAX)
- , m_maximum(DBL_MAX)
- , m_enabled(true)
-{
-}
-
-void QQuickDragAxis::setMinimum(qreal minimum)
-{
- if (m_minimum == minimum)
- return;
-
- m_minimum = minimum;
- emit minimumChanged();
-}
-
-void QQuickDragAxis::setMaximum(qreal maximum)
-{
- if (m_maximum == maximum)
- return;
-
- m_maximum = maximum;
- emit maximumChanged();
-}
-
-void QQuickDragAxis::setEnabled(bool enabled)
-{
- if (m_enabled == enabled)
- return;
-
- m_enabled = enabled;
- emit enabledChanged();
-}
/*!
\readonly
diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h
index 363df31a64..7ba1021924 100644
--- a/src/quick/handlers/qquickdraghandler_p.h
+++ b/src/quick/handlers/qquickdraghandler_p.h
@@ -52,39 +52,10 @@
//
#include "qquicksinglepointhandler_p.h"
+#include "qquickdragaxis_p.h"
QT_BEGIN_NAMESPACE
-class Q_AUTOTEST_EXPORT QQuickDragAxis : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(qreal minimum READ minimum WRITE setMinimum NOTIFY minimumChanged)
- Q_PROPERTY(qreal maximum READ maximum WRITE setMaximum NOTIFY maximumChanged)
- Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
-
-public:
- QQuickDragAxis();
-
- qreal minimum() const { return m_minimum; }
- void setMinimum(qreal minimum);
-
- qreal maximum() const { return m_maximum; }
- void setMaximum(qreal maximum);
-
- bool enabled() const { return m_enabled; }
- void setEnabled(bool enabled);
-
-signals:
- void minimumChanged();
- void maximumChanged();
- void enabledChanged();
-
-private:
- qreal m_minimum;
- qreal m_maximum;
- bool m_enabled;
-};
-
class Q_AUTOTEST_EXPORT QQuickDragHandler : public QQuickSinglePointHandler
{
Q_OBJECT
diff --git a/src/quick/handlers/qquickhandlerpoint.cpp b/src/quick/handlers/qquickhandlerpoint.cpp
new file mode 100644
index 0000000000..4bd5d2cbfb
--- /dev/null
+++ b/src/quick/handlers/qquickhandlerpoint.cpp
@@ -0,0 +1,351 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickhandlerpoint_p.h"
+#include "private/qquickevents_p_p.h"
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH_TARGET)
+
+/*!
+ \qmltype HandlerPoint
+ \instantiates QQuickHandlerPoint
+ \inqmlmodule Qt.labs.handlers
+ \ingroup qtquick-handlers
+ \brief An event point.
+
+ A QML representation of a QQuickEventPoint.
+
+ It's possible to make bindings to properties of a \l SinglePointHandler's
+ current point. For example:
+
+ \snippet pointerHandlers/dragHandlerNullTarget.qml 0
+
+ The point is kept up-to-date when the DragHandler is actively responding to
+ an EventPoint; but when the point is released, or the current point is
+ being handled by a different handler, \c position.x and \c position.y are 0.
+
+ \note This is practically identical to QtQuick::EventPoint; however an
+ EventPoint is a long-lived QObject which is invalidated between gestures
+ and reused for subsequent event deliveries. Continuous bindings to its
+ properties are not possible, and an individual handler cannot rely on it
+ outside the period when that point is part of an active gesture which that
+ handler is handling. HandlerPoint is a Q_GADGET that the handler owns.
+ This allows you to make lifetime bindings to its properties.
+
+ \sa SinglePointHandler::point
+*/
+
+QQuickHandlerPoint::QQuickHandlerPoint()
+ : m_id(0)
+ , m_rotation(0)
+ , m_pressure(0)
+{}
+
+void QQuickHandlerPoint::reset()
+{
+ m_id = 0;
+ m_uniqueId = QPointingDeviceUniqueId();
+ m_position = QPointF();
+ m_scenePosition = QPointF();
+ m_pressPosition = QPointF();
+ m_scenePressPosition = QPointF();
+ m_sceneGrabPosition = QPointF();
+ m_velocity = QVector2D();
+ m_rotation = 0;
+ m_pressure = 0;
+ m_ellipseDiameters = QSizeF();
+ m_pressedButtons = Qt::NoButton;
+ m_pressedModifiers = Qt::NoModifier;
+}
+
+void QQuickHandlerPoint::reset(const QQuickEventPoint *point)
+{
+ m_id = point->pointId();
+ const QQuickPointerEvent *event = point->pointerEvent();
+ switch (point->state()) {
+ case QQuickEventPoint::Pressed:
+ m_pressPosition = point->position();
+ m_scenePressPosition = point->scenePosition();
+ m_pressedButtons = event->buttons();
+ break;
+ case QQuickEventPoint::Released:
+ reset();
+ return;
+ default:
+ m_pressedButtons = event->buttons();
+ break;
+ }
+ m_pressedModifiers = event->modifiers();
+ if (event->asPointerTouchEvent()) {
+ const QQuickEventTouchPoint *tp = static_cast<const QQuickEventTouchPoint *>(point);
+ m_uniqueId = tp->uniqueId();
+ m_rotation = tp->rotation();
+ m_pressure = tp->pressure();
+ m_ellipseDiameters = tp->ellipseDiameters();
+ } else if (event->asPointerTabletEvent()) {
+ // TODO
+ } else {
+ m_uniqueId = event->device()->uniqueId();
+ m_rotation = 0;
+ m_pressure = event->buttons() ? 1 : 0;
+ m_ellipseDiameters = QSizeF();
+ }
+ m_position = point->position();
+ m_scenePosition = point->scenePosition();
+ if (point->state() == QQuickEventPoint::Updated)
+ m_velocity = point->velocity();
+}
+
+void QQuickHandlerPoint::reset(const QVector<QQuickEventPoint *> &points)
+{
+ if (points.isEmpty()) {
+ qWarning("reset: no points");
+ return;
+ }
+ if (points.count() == 1) {
+ reset(points.first());
+ return;
+ }
+ // all points are required to be from the same event
+ const QQuickPointerEvent *event = points.first()->pointerEvent();
+ QPointF posSum;
+ QPointF scenePosSum;
+ QVector2D velocitySum;
+ qreal pressureSum = 0;
+ QSizeF ellipseDiameterSum;
+ bool press = false;
+ const QQuickPointerTouchEvent *touchEvent = event->asPointerTouchEvent();
+ for (const QQuickEventPoint *point : qAsConst(points)) {
+ posSum += point->position();
+ scenePosSum += point->scenePosition();
+ velocitySum += point->velocity();
+ if (touchEvent) {
+ pressureSum += static_cast<const QQuickEventTouchPoint *>(point)->pressure();
+ ellipseDiameterSum += static_cast<const QQuickEventTouchPoint *>(point)->ellipseDiameters();
+ }
+ if (point->state() == QQuickEventPoint::Pressed)
+ press = true;
+ }
+ m_id = 0;
+ m_uniqueId = QPointingDeviceUniqueId();
+ m_pressedButtons = event->buttons();
+ m_pressedModifiers = event->modifiers();
+ m_position = posSum / points.size();
+ m_scenePosition = scenePosSum / points.size();
+ if (press) {
+ m_pressPosition = m_position;
+ m_scenePressPosition = m_scenePosition;
+ }
+ m_velocity = velocitySum / points.size();
+ m_rotation = 0; // averaging the rotations of all the points isn't very sensible
+ m_pressure = pressureSum / points.size();
+ m_ellipseDiameters = ellipseDiameterSum / points.size();
+}
+
+/*!
+ \readonly
+ \qmlproperty int QtQuick::HandlerPoint::id
+ \brief The ID number of the point
+
+ During a touch gesture, from the time that the first finger is pressed
+ until the last finger is released, each touchpoint will have a unique ID
+ number. Likewise, if input from multiple devices occurs (for example
+ simultaneous mouse and touch presses), all the current event points from
+ all the devices will have unique IDs.
+
+ \note Do not assume that id numbers start at zero or that they are
+ sequential. Such an assumption is often false due to the way the underlying
+ drivers work.
+
+ \sa QTouchEvent::TouchPoint::id
+*/
+
+/*!
+ \readonly
+ \qmlproperty PointingDeviceUniqueId QtQuick::HandlerPoint::uniqueId
+ \brief The unique ID of the point, if any
+
+ This is normally empty, because touchscreens cannot uniquely identify fingers.
+
+ On some types of touchscreens, especially those using TUIO drivers,
+ it's possible to use recognizable physical tokens (fiducial objects)
+ in addition to fingers. So if this point is a touch point, and
+ uniqueId is set, it is the identifier for such an object.
+
+ On a graphics tablet, each type of stylus or other tool often has a unique
+ ID or serial number, which can be useful to respond in different ways to
+ different tools.
+
+ Interpreting the contents of this ID requires knowledge of the hardware and
+ drivers in use.
+
+ \sa QTabletEvent::uniqueId, QtQuick::TouchPoint::uniqueId, QtQuick::EventTouchPoint::uniqueId
+*/
+
+/*!
+ \readonly
+ \qmlproperty QPointF QtQuick::HandlerPoint::position
+ \brief The position within the \c parent Item
+
+ This is the position of the event point relative to the bounds of
+ the \l {PointerHandler::parent} {parent}.
+*/
+
+/*!
+ \readonly
+ \qmlproperty QPointF QtQuick::HandlerPoint::scenePosition
+ \brief The position within the scene
+
+ This is the position of the event point relative to the bounds of the Qt
+ Quick scene (typically the whole window).
+*/
+
+/*!
+ \readonly
+ \qmlproperty QPointF QtQuick::HandlerPoint::pressPosition
+ \brief The pressed position within the \c parent Item
+
+ This is the position at which this point was pressed, relative to the
+ bounds of the \l {PointerHandler::parent} {parent}.
+*/
+
+/*!
+ \readonly
+ \qmlproperty QPointF QtQuick::HandlerPoint::scenePressPosition
+ \brief The pressed position within the scene
+
+ This is the position at which this point was pressed, in the coordinate
+ system of the \l {Qt Quick Scene Graph}{scene graph}.
+*/
+
+/*!
+ \readonly
+ \qmlproperty QPointF QtQuick::HandlerPoint::sceneGrabPosition
+ \brief The grabbed position within the scene
+
+ If this point has been grabbed by a Pointer Handler or an Item, it means
+ that object has taken sole responsibility for handling the movement and the
+ release if this point. In that case, this is the position at which the grab
+ occurred, in the coordinate system of the \l {Qt Quick Scene Graph}{scene graph}.
+*/
+
+/*!
+ \readonly
+ \qmlproperty enum QtQuick::HandlerPoint::pressedButtons
+ \brief Which mouse or stylus buttons are currently pressed
+
+ \sa MouseArea::pressedButtons
+*/
+
+/*!
+ \readonly
+ \qmlproperty enum QtQuick::HandlerPoint::modifiers
+ \brief Which modifier keys are currently pressed
+
+ This property holds the keyboard modifiers that were pressed at the time
+ the event occurred.
+
+ \sa MouseArea::modifiers
+*/
+
+/*!
+ \readonly
+ \qmlproperty QVector2D QtQuick::HandlerPoint::velocity
+ \brief A vector representing the average speed and direction of movement
+
+ This is a velocity vector pointing in the direction of movement, in logical
+ pixels per second. It has x and y components, at least one of which will be
+ nonzero when this point is in motion. It holds the average recent velocity:
+ how fast and in which direction the event point has been moving recently.
+
+ \sa QtQuick::EventPoint::velocity, QtQuick::TouchPoint::velocity, QTouchEvent::TouchPoint::velocity
+*/
+
+/*!
+ \readonly
+ \qmlproperty qreal QtQuick::HandlerPoint::rotation
+
+ This property holds the rotation angle of the stylus on a graphics tablet
+ or the contact patch of a touchpoint on a touchscreen.
+
+ It is valid only with certain tablet stylus devices and touchscreens that
+ can measure the rotation angle. Otherwise, it will be zero.
+*/
+
+/*!
+ \readonly
+ \qmlproperty qreal QtQuick::HandlerPoint::pressure
+
+ This property tells how hard the user is pressing the stylus on a graphics
+ tablet or the finger against a touchscreen, in the range from \c 0 (no
+ measurable pressure) to \c 1.0 (maximum pressure which the device can
+ measure).
+
+ It is valid only with certain tablets and touchscreens that can measure
+ pressure. Otherwise, it will be zero.
+*/
+
+/*!
+ \readonly
+ \qmlproperty size QtQuick::HandlerPoint::ellipseDiameters
+
+ This property holds the diameters of the contact patch, if the event
+ comes from a touchpoint and the device provides this information.
+
+ A touchpoint is modeled as an elliptical area where the finger is pressed
+ against the touchscreen. (In fact, it could also be modeled as a bitmap;
+ but in that case we expect an elliptical bounding estimate to be fitted to
+ the contact patch before the event is sent.) The harder the user presses,
+ the larger the contact patch; so, these diameters provide an alternate way
+ of detecting pressure, in case the device does not include a separate
+ pressure sensor. The ellipse is centered on \l scenePosition (\l position
+ in the PointerHandler's Item's local coordinates). The \l rotation property
+ provides the rotation of the ellipse, if known. It is expected that if the
+ \l rotation is zero, the \l {QSize::height}{height} is the larger dimension
+ (the major axis), because of the usual hand position, reaching upward or
+ outward across the surface.
+
+ If the contact patch is unknown, or the device is not a touchscreen,
+ these values will be zero.
+
+ \sa QtQuick::EventTouchPoint::ellipseDiameters, QtQuick::TouchPoint::ellipseDiameters, QTouchEvent::TouchPoint::ellipseDiameters
+*/
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickhandlerpoint_p.h b/src/quick/handlers/qquickhandlerpoint_p.h
new file mode 100644
index 0000000000..1dff52942a
--- /dev/null
+++ b/src/quick/handlers/qquickhandlerpoint_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKHANDLERPOINT_H
+#define QQUICKHANDLERPOINT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickpointerdevicehandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickMultiPointHandler;
+class QQuickSinglePointHandler;
+
+class Q_QUICK_PRIVATE_EXPORT QQuickHandlerPoint {
+ Q_GADGET
+ Q_PROPERTY(int id READ id)
+ Q_PROPERTY(QPointingDeviceUniqueId uniqueId READ uniqueId)
+ Q_PROPERTY(QPointF position READ position)
+ Q_PROPERTY(QPointF scenePosition READ scenePosition)
+ Q_PROPERTY(QPointF pressPosition READ pressPosition)
+ Q_PROPERTY(QPointF scenePressPosition READ scenePressPosition)
+ Q_PROPERTY(QPointF sceneGrabPosition READ sceneGrabPosition)
+ Q_PROPERTY(Qt::MouseButtons pressedButtons READ pressedButtons)
+ Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers)
+ Q_PROPERTY(QVector2D velocity READ velocity)
+ Q_PROPERTY(qreal rotation READ rotation)
+ Q_PROPERTY(qreal pressure READ pressure)
+ Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters)
+
+public:
+ QQuickHandlerPoint();
+
+ int id() const { return m_id; }
+ Qt::MouseButtons pressedButtons() const { return m_pressedButtons; }
+ Qt::KeyboardModifiers modifiers() const { return m_pressedModifiers; }
+ QPointF pressPosition() const { return m_pressPosition; }
+ QPointF scenePressPosition() const { return m_scenePressPosition; }
+ QPointF sceneGrabPosition() const { return m_sceneGrabPosition; }
+ QPointF position() const { return m_position; }
+ QPointF scenePosition() const { return m_scenePosition; }
+ QVector2D velocity() const { return m_velocity; }
+ qreal rotation() const { return m_rotation; }
+ qreal pressure() const { return m_pressure; }
+ QSizeF ellipseDiameters() const { return m_ellipseDiameters; }
+ QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; }
+
+ void reset();
+ void reset(const QQuickEventPoint *point);
+ void reset(const QVector<QQuickEventPoint *> &points);
+
+private:
+ int m_id;
+ QPointingDeviceUniqueId m_uniqueId;
+ Qt::MouseButtons m_pressedButtons;
+ Qt::KeyboardModifiers m_pressedModifiers;
+ QPointF m_position;
+ QPointF m_scenePosition;
+ QPointF m_pressPosition;
+ QPointF m_scenePressPosition;
+ QPointF m_sceneGrabPosition;
+ QVector2D m_velocity;
+ qreal m_rotation;
+ qreal m_pressure;
+ QSizeF m_ellipseDiameters;
+ friend class QQuickMultiPointHandler;
+ friend class QQuickSinglePointHandler;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickHandlerPoint)
+
+#endif // QQUICKHANDLERPOINT_H
diff --git a/src/quick/handlers/qquickhandlersmodule.cpp b/src/quick/handlers/qquickhandlersmodule.cpp
index c9e08fe4f2..814602c0bd 100644
--- a/src/quick/handlers/qquickhandlersmodule.cpp
+++ b/src/quick/handlers/qquickhandlersmodule.cpp
@@ -40,6 +40,7 @@
#include "qquickhandlersmodule_p.h"
#include "qquickpointerhandler_p.h"
#include "qquickdraghandler_p.h"
+#include "qquickhoverhandler_p.h"
#include "qquickpinchhandler_p.h"
#include "qquickpointhandler_p.h"
#include "qquicktaphandler_p.h"
@@ -87,6 +88,7 @@ static void qt_quickhandlers_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickDragHandler>(uri,major,minor,"DragHandler");
qmlRegisterUncreatableType<QQuickDragAxis>(uri, major, minor, "DragAxis",
QQuickDragHandler::tr("DragAxis is only available as a grouped property of DragHandler"));
+ qmlRegisterType<QQuickHoverHandler>(uri,major,minor,"HoverHandler");
qmlRegisterType<QQuickPinchHandler>(uri,major,minor,"PinchHandler");
qmlRegisterType<QQuickTapHandler>(uri,major,minor,"TapHandler");
qRegisterMetaType<QQuickHandlerPoint>();
diff --git a/src/quick/handlers/qquickhoverhandler.cpp b/src/quick/handlers/qquickhoverhandler.cpp
new file mode 100644
index 0000000000..430722bd93
--- /dev/null
+++ b/src/quick/handlers/qquickhoverhandler.cpp
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickhoverhandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcHoverHandler, "qt.quick.handler.hover")
+
+/*!
+ \qmltype HoverHandler
+ \instantiates QQuickHoverHandler
+ \inqmlmodule QtQuick
+ \ingroup qtquick-handlers
+ \brief Handler for mouse and tablet hover
+
+ HoverHandler detects a hovering cursor. Since touchscreens don't generally
+ offer hover events, in practice it detects a hovering mouse or tablet stylus.
+
+ \sa MouseArea
+*/
+
+QQuickHoverHandler::QQuickHoverHandler(QObject *parent)
+ : QQuickSinglePointHandler(parent)
+ , m_hovered(false)
+{
+ // Rule out the touchscreen for now (can be overridden in QML in case a hover-detecting touchscreen exists)
+ setAcceptedDevices(static_cast<QQuickPointerDevice::DeviceType>(
+ static_cast<int>(QQuickPointerDevice::AllDevices) ^ static_cast<int>(QQuickPointerDevice::TouchScreen)));
+}
+
+QQuickHoverHandler::~QQuickHoverHandler()
+{
+ QQuickItemPrivate *parItemPrivate = QQuickItemPrivate::get(parentItem());
+ parItemPrivate->setHasHoverInChild(false);
+}
+
+void QQuickHoverHandler::componentComplete()
+{
+ parentItem()->setAcceptHoverEvents(true);
+ QQuickItemPrivate::get(parentItem())->setHasHoverInChild(true);
+}
+
+bool QQuickHoverHandler::wantsPointerEvent(QQuickPointerEvent *event)
+{
+ QQuickEventPoint *point = event->point(0);
+ if (QQuickPointerDeviceHandler::wantsPointerEvent(event) && wantsEventPoint(point)) {
+ // assume this is a mouse event, so there's only one point
+ setPointId(point->pointId());
+ return true;
+ }
+ setHovered(false);
+ return false;
+}
+
+void QQuickHoverHandler::handleEventPoint(QQuickEventPoint *point)
+{
+ setHovered(true);
+ setPassiveGrab(point);
+}
+
+void QQuickHoverHandler::setHovered(bool hovered)
+{
+ if (m_hovered != hovered) {
+ qCDebug(lcHoverHandler) << objectName() << "hovered" << m_hovered << "->" << hovered;
+ m_hovered = hovered;
+ emit hoveredChanged();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickhoverhandler_p.h b/src/quick/handlers/qquickhoverhandler_p.h
new file mode 100644
index 0000000000..876d01761b
--- /dev/null
+++ b/src/quick/handlers/qquickhoverhandler_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKHOVERHANDLER_H
+#define QQUICKHOVERHANDLER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickitem.h"
+#include "qevent.h"
+#include "qquicksinglepointhandler_p.h"
+#include <QtCore/qbasictimer.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickHoverHandler : public QQuickSinglePointHandler
+{
+ Q_OBJECT
+ Q_PROPERTY(bool hovered READ isHovered NOTIFY hoveredChanged)
+
+public:
+ explicit QQuickHoverHandler(QObject *parent = 0);
+ ~QQuickHoverHandler();
+
+ bool isHovered() const { return m_hovered; }
+
+Q_SIGNALS:
+ void hoveredChanged();
+
+protected:
+ void componentComplete() override;
+ bool wantsPointerEvent(QQuickPointerEvent *event) override;
+ void handleEventPoint(QQuickEventPoint *point) override;
+
+private:
+ void setHovered(bool hovered);
+
+private:
+ bool m_hovered;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickHoverHandler)
+
+#endif // QQUICKHOVERHANDLER_H
diff --git a/src/quick/handlers/qquickmultipointhandler.cpp b/src/quick/handlers/qquickmultipointhandler.cpp
index d595b4c9b4..9a3732ff84 100644
--- a/src/quick/handlers/qquickmultipointhandler.cpp
+++ b/src/quick/handlers/qquickmultipointhandler.cpp
@@ -63,7 +63,6 @@ QQuickMultiPointHandler::QQuickMultiPointHandler(QObject *parent, int minimumPoi
: QQuickPointerDeviceHandler(parent)
, m_minimumPointCount(minimumPointCount)
, m_maximumPointCount(-1)
- , m_pointDistanceThreshold(0)
{
}
@@ -81,7 +80,7 @@ bool QQuickMultiPointHandler::wantsPointerEvent(QQuickPointerEvent *event)
return true;
#endif
- if (sameAsCurrentPoints(event))
+ if (hasCurrentPoints(event))
return true;
const QVector<QQuickEventPoint *> candidatePoints = eligiblePoints(event);
@@ -91,12 +90,24 @@ bool QQuickMultiPointHandler::wantsPointerEvent(QQuickPointerEvent *event)
return ret;
}
+void QQuickMultiPointHandler::handlePointerEventImpl(QQuickPointerEvent *event)
+{
+ QQuickPointerHandler::handlePointerEventImpl(event);
+ m_centroid.reset(m_currentPoints);
+ emit centroidChanged();
+}
+
+void QQuickMultiPointHandler::onActiveChanged()
+{
+ if (active()) {
+ m_centroid.m_sceneGrabPosition = m_centroid.m_scenePosition;
+ }
+}
+
QVector<QQuickEventPoint *> QQuickMultiPointHandler::eligiblePoints(QQuickPointerEvent *event)
{
QVector<QQuickEventPoint *> ret;
int c = event->pointCount();
- QRectF parentBounds = parentItem()->mapRectToScene(parentItem()->boundingRect())
- .marginsAdded(QMarginsF(m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold));
// If one or more points are newly pressed or released, all non-released points are candidates for this handler.
// In other cases however, do not steal the grab: that is, if a point has a grabber,
// it's not a candidate for this handler.
@@ -108,7 +119,7 @@ QVector<QQuickEventPoint *> QQuickMultiPointHandler::eligiblePoints(QQuickPointe
if (exclusiveGrabber && exclusiveGrabber != this)
continue;
}
- if (p->state() != QQuickEventPoint::Released && parentBounds.contains(p->scenePosition()))
+ if (p->state() != QQuickEventPoint::Released && wantsEventPoint(p))
ret << p;
}
return ret;
@@ -122,7 +133,7 @@ QVector<QQuickEventPoint *> QQuickMultiPointHandler::eligiblePoints(QQuickPointe
If a smaller number of touchpoints are in contact with the
\l {PointerHandler::parent}{parent}, they will be ignored.
- Any ignored points are eligible to activate other Pointer Handlers that
+ Any ignored points are eligible to activate other Input Handlers that
have different constraints, on the same Item or on other Items.
The default value is 2.
@@ -148,7 +159,7 @@ void QQuickMultiPointHandler::setMinimumPointCount(int c)
chosen in the order that they are pressed, and the remaining points will
be ignored.
- Any ignored points are eligible to activate other Pointer Handlers that
+ Any ignored points are eligible to activate other Input Handlers that
have different constraints, on the same Item or on other Items.
The default value is the same as \l minimumPointCount.
@@ -162,34 +173,11 @@ void QQuickMultiPointHandler::setMaximumPointCount(int maximumPointCount)
emit maximumPointCountChanged();
}
-/*!
- \qmlproperty real MultiPointHandler::pointDistanceThreshold
-
- The margin beyond the bounds of the \l {PointerHandler::parent}{parent}
- item within which a touch point can activate this handler. For example, on
- a PinchHandler where the \l {PointerHandler::target}{target} is also the
- \c parent, it's useful to set this to a distance at least half the width
- of a typical user's finger, so that if the \c parent has been scaled down
- to a very small size, the pinch gesture is still possible.
-
- The default value is 0.
-
- \image pointDistanceThreshold.png
-*/
-void QQuickMultiPointHandler::setPointDistanceThreshold(qreal pointDistanceThreshold)
-{
- if (m_pointDistanceThreshold == pointDistanceThreshold)
- return;
-
- m_pointDistanceThreshold = pointDistanceThreshold;
- emit pointDistanceThresholdChanged();
-}
-
-bool QQuickMultiPointHandler::sameAsCurrentPoints(QQuickPointerEvent *event)
+bool QQuickMultiPointHandler::hasCurrentPoints(QQuickPointerEvent *event)
{
bool ret = true;
int c = event->pointCount();
- if (c != m_currentPoints.size())
+ if (c < m_currentPoints.size())
return false;
// TODO optimize: either ensure the points are sorted,
// or use std::equal with a predicate
@@ -207,27 +195,6 @@ bool QQuickMultiPointHandler::sameAsCurrentPoints(QQuickPointerEvent *event)
return ret;
}
-// TODO make templates for these functions somehow?
-QPointF QQuickMultiPointHandler::touchPointCentroid()
-{
- QPointF ret;
- if (Q_UNLIKELY(m_currentPoints.size() == 0))
- return ret;
- for (QQuickEventPoint *point : qAsConst(m_currentPoints))
- ret += point->scenePosition();
- return ret / m_currentPoints.size();
-}
-
-QVector2D QQuickMultiPointHandler::touchPointCentroidVelocity()
-{
- QVector2D ret;
- if (Q_UNLIKELY(m_currentPoints.size() == 0))
- return ret;
- for (QQuickEventPoint *point : qAsConst(m_currentPoints))
- ret += point->velocity();
- return ret / m_currentPoints.size();
-}
-
qreal QQuickMultiPointHandler::averageTouchPointDistance(const QPointF &ref)
{
qreal ret = 0;
diff --git a/src/quick/handlers/qquickmultipointhandler_p.h b/src/quick/handlers/qquickmultipointhandler_p.h
index 67e550d387..06045771fb 100644
--- a/src/quick/handlers/qquickmultipointhandler_p.h
+++ b/src/quick/handlers/qquickmultipointhandler_p.h
@@ -53,6 +53,7 @@
#include "qquickitem.h"
#include "qevent.h"
+#include "qquickhandlerpoint_p.h"
#include "qquickpointerdevicehandler_p.h"
QT_BEGIN_NAMESPACE
@@ -62,7 +63,7 @@ class Q_AUTOTEST_EXPORT QQuickMultiPointHandler : public QQuickPointerDeviceHand
Q_OBJECT
Q_PROPERTY(int minimumPointCount READ minimumPointCount WRITE setMinimumPointCount NOTIFY minimumPointCountChanged)
Q_PROPERTY(int maximumPointCount READ maximumPointCount WRITE setMaximumPointCount NOTIFY maximumPointCountChanged)
- Q_PROPERTY(qreal pointDistanceThreshold READ pointDistanceThreshold WRITE setPointDistanceThreshold NOTIFY pointDistanceThresholdChanged)
+ Q_PROPERTY(QQuickHandlerPoint centroid READ centroid NOTIFY centroidChanged)
public:
explicit QQuickMultiPointHandler(QObject *parent = nullptr, int minimumPointCount = 2);
@@ -74,13 +75,13 @@ public:
int maximumPointCount() const { return m_maximumPointCount >= 0 ? m_maximumPointCount : m_minimumPointCount; }
void setMaximumPointCount(int maximumPointCount);
- qreal pointDistanceThreshold() const { return m_pointDistanceThreshold; }
- void setPointDistanceThreshold(qreal pointDistanceThreshold);
+ QQuickHandlerPoint centroid() const { return m_centroid; }
signals:
void minimumPointCountChanged();
void maximumPointCountChanged();
void pointDistanceThresholdChanged();
+ void centroidChanged();
protected:
struct PointData {
@@ -91,10 +92,10 @@ protected:
};
bool wantsPointerEvent(QQuickPointerEvent *event) override;
- bool sameAsCurrentPoints(QQuickPointerEvent *event);
+ void handlePointerEventImpl(QQuickPointerEvent *event) override;
+ void onActiveChanged() override;
+ bool hasCurrentPoints(QQuickPointerEvent *event);
QVector<QQuickEventPoint *> eligiblePoints(QQuickPointerEvent *event);
- QPointF touchPointCentroid();
- QVector2D touchPointCentroidVelocity();
qreal averageTouchPointDistance(const QPointF &ref);
qreal averageStartingDistance(const QPointF &ref);
qreal averageTouchPointAngle(const QPointF &ref);
@@ -106,9 +107,9 @@ protected:
protected:
QVector<QQuickEventPoint *> m_currentPoints;
+ QQuickHandlerPoint m_centroid;
int m_minimumPointCount;
int m_maximumPointCount;
- qreal m_pointDistanceThreshold;
};
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp
index 57d851ed59..c251ae6d36 100644
--- a/src/quick/handlers/qquickpinchhandler.cpp
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -61,7 +61,7 @@ Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch")
\brief Handler for pinch gestures.
PinchHandler is a handler that interprets a multi-finger gesture to
- interactively rotate, zoom, and drag an Item. Like other Pointer Handlers,
+ interactively rotate, zoom, and drag an Item. Like other Input Handlers,
by default it is fully functional, and manipulates its \l target,
which is the Item within which it is declared.
@@ -88,6 +88,7 @@ Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch")
QQuickPinchHandler::QQuickPinchHandler(QObject *parent)
: QQuickMultiPointHandler(parent, 2)
, m_activeScale(1)
+ , m_accumulatedScale(1)
, m_activeRotation(0)
, m_activeTranslation(0,0)
, m_minimumScale(-qInf())
@@ -287,11 +288,11 @@ bool QQuickPinchHandler::wantsPointerEvent(QQuickPointerEvent *event)
void QQuickPinchHandler::onActiveChanged()
{
+ QQuickMultiPointHandler::onActiveChanged();
if (active()) {
m_startMatrix = QMatrix4x4();
- m_startCentroid = touchPointCentroid();
- m_startAngles = angles(m_startCentroid);
- m_startDistance = averageTouchPointDistance(m_startCentroid);
+ m_startAngles = angles(m_centroid.sceneGrabPosition());
+ m_startDistance = averageTouchPointDistance(m_centroid.sceneGrabPosition());
m_activeRotation = 0;
m_activeTranslation = QVector2D();
if (const QQuickItem *t = target()) {
@@ -304,7 +305,7 @@ void QQuickPinchHandler::onActiveChanged()
m_startMatrix.rotate(m_startRotation, 0, 0, -1);
m_startMatrix.translate(-xformOrigin);
} else {
- m_startScale = 1;
+ m_startScale = m_accumulatedScale;
m_startRotation = 0;
}
qCInfo(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation;
@@ -319,6 +320,7 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
for (QQuickEventPoint *point : qAsConst(m_currentPoints))
qCDebug(lcPinchHandler) << point->state() << point->sceneGrabPosition() << "->" << point->scenePosition();
}
+ QQuickMultiPointHandler::handlePointerEventImpl(event);
qreal dist = 0;
#if QT_CONFIG(gestures)
@@ -328,8 +330,7 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
m_activeScale = 1;
m_activeRotation = 0;
m_activeTranslation = QVector2D();
- m_centroid = QPointF();
- m_centroidVelocity = QVector2D();
+ m_centroid.reset();
setActive(false);
emit updated();
return;
@@ -344,12 +345,9 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
return;
}
if (!active()) {
- m_centroid = gesture->point(0)->scenePosition();
setActive(true);
- m_startCentroid = m_centroid;
// Native gestures for 2-finger pinch do not allow dragging, so
// the centroid won't move during the gesture, and translation stays at zero
- m_centroidVelocity = QVector2D();
m_activeTranslation = QVector2D();
}
} else
@@ -374,17 +372,15 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
return;
}
// TODO check m_pinchOrigin: right now it acts like it's set to PinchCenter
- m_centroid = touchPointCentroid();
- m_centroidVelocity = touchPointCentroidVelocity();
// avoid mapping the minima and maxima, as they might have unmappable values
// such as -inf/+inf. Because of this we perform the bounding to min/max in local coords.
// 1. scale
- dist = averageTouchPointDistance(m_centroid);
+ dist = averageTouchPointDistance(m_centroid.scenePosition());
m_activeScale = dist / m_startDistance;
m_activeScale = qBound(m_minimumScale/m_startScale, m_activeScale, m_maximumScale/m_startScale);
// 2. rotate
- QVector<PointData> newAngles = angles(m_centroid);
+ QVector<PointData> newAngles = angles(m_centroid.scenePosition());
const qreal angleDelta = averageAngleDelta(m_startAngles, newAngles);
m_activeRotation += angleDelta;
m_startAngles = std::move(newAngles);
@@ -396,20 +392,19 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
QPointF centroidParentPos;
QRectF bounds(m_minimumX, m_minimumY, m_maximumX - m_minimumX, m_maximumY - m_minimumY);
if (target() && target()->parentItem()) {
- centroidParentPos = target()->parentItem()->mapFromScene(m_centroid);
+ centroidParentPos = target()->parentItem()->mapFromScene(m_centroid.scenePosition());
centroidParentPos = QPointF(qBound(bounds.left(), centroidParentPos.x(), bounds.right()),
qBound(bounds.top(), centroidParentPos.y(), bounds.bottom()));
}
const qreal totalRotation = m_startRotation + m_activeRotation;
const qreal rotation = qBound(m_minimumRotation, totalRotation, m_maximumRotation);
m_activeRotation += (rotation - totalRotation); //adjust for the potential bounding above
- const qreal scale = m_startScale * m_activeScale;
+ m_accumulatedScale = m_startScale * m_activeScale;
if (target() && target()->parentItem()) {
// 3. Drag/translate
- const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(m_startCentroid);
+ const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(m_centroid.sceneGrabPosition());
m_activeTranslation = QVector2D(centroidParentPos - centroidStartParentPos);
-
// apply rotation + scaling around the centroid - then apply translation.
QMatrix4x4 mat;
@@ -428,16 +423,16 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
target()->setPosition(pos);
target()->setRotation(rotation);
- target()->setScale(scale);
+ target()->setScale(m_accumulatedScale);
// TODO some translation inadvertently happens; try to hold the chosen pinch origin in place
} else {
- m_activeTranslation = QVector2D(m_centroid - m_startCentroid);
+ m_activeTranslation = QVector2D(m_centroid.scenePosition() - m_centroid.scenePressPosition());
}
- qCDebug(lcPinchHandler) << "centroid" << m_startCentroid << "->" << m_centroid
+ qCDebug(lcPinchHandler) << "centroid" << m_centroid.scenePressPosition() << "->" << m_centroid.scenePosition()
<< ", distance" << m_startDistance << "->" << dist
- << ", startScale" << m_startScale << "->" << scale
+ << ", startScale" << m_startScale << "->" << m_accumulatedScale
<< ", activeRotation" << m_activeRotation
<< ", rotation" << rotation
<< " from " << event->device()->type();
@@ -456,22 +451,25 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
/*!
\readonly
- \qmlproperty QVector2D QtQuick::PinchHandler::centroidVelocity
+ \qmlproperty real QtQuick::PinchHandler::scale
- The average velocity of the \l centroid: a vector representing the speed
- and direction of movement of the whole group of touchpoints, in logical
- pixels per second.
+ The scale factor that will automatically be set on the \l target if it is not null.
+ Otherwise, bindings can be used to do arbitrary things with this value.
+ While the pinch gesture is being performed, it is continuously multiplied by
+ \l activeScale; after the gesture ends, it stays the same; and when the next
+ pinch gesture begins, it begins to be multiplied by activeScale again.
*/
/*!
\readonly
- \qmlproperty real QtQuick::PinchHandler::scale
-
- The scale factor. It is 1.0 when the gesture begins, increases as the
- touchpoints are spread apart, and decreases as the touchpoints are brought
- together. If \l target is not null, this will be automatically applied to its
- \l {Item::scale}{scale}. Otherwise, bindings can be used to do arbitrary
- things with this value.
+ \qmlproperty real QtQuick::PinchHandler::activeScale
+
+ The scale factor while the pinch gesture is being performed.
+ It is 1.0 when the gesture begins, increases as the touchpoints are spread
+ apart, and decreases as the touchpoints are brought together.
+ If \l target is not null, its \l {Item::scale}{scale} will be automatically
+ multiplied by this value.
+ Otherwise, bindings can be used to do arbitrary things with this value.
*/
/*!
diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h
index 9a17971416..0d0630d2a9 100644
--- a/src/quick/handlers/qquickpinchhandler_p.h
+++ b/src/quick/handlers/qquickpinchhandler_p.h
@@ -66,9 +66,8 @@ class Q_AUTOTEST_EXPORT QQuickPinchHandler : public QQuickMultiPointHandler
Q_PROPERTY(qreal minimumRotation READ minimumRotation WRITE setMinimumRotation NOTIFY minimumRotationChanged)
Q_PROPERTY(qreal maximumRotation READ maximumRotation WRITE setMaximumRotation NOTIFY maximumRotationChanged)
Q_PROPERTY(PinchOrigin pinchOrigin READ pinchOrigin WRITE setPinchOrigin NOTIFY pinchOriginChanged)
- Q_PROPERTY(QPointF centroid READ centroid NOTIFY updated)
- Q_PROPERTY(QVector2D centroidVelocity READ centroidVelocity NOTIFY updated)
Q_PROPERTY(qreal scale READ scale NOTIFY updated)
+ Q_PROPERTY(qreal activeScale READ activeScale NOTIFY updated)
Q_PROPERTY(qreal rotation READ rotation NOTIFY updated)
Q_PROPERTY(QVector2D translation READ translation NOTIFY updated)
Q_PROPERTY(qreal minimumX READ minimumX WRITE setMinimumX NOTIFY minimumXChanged)
@@ -101,11 +100,9 @@ public:
void setPinchOrigin(PinchOrigin pinchOrigin);
QVector2D translation() const { return m_activeTranslation; }
- qreal scale() const { return m_activeScale; }
+ qreal scale() const { return m_accumulatedScale; }
+ qreal activeScale() const { return m_activeScale; }
qreal rotation() const { return m_activeRotation; }
- QPointF centroid() const { return m_centroid; }
- QVector2D centroidVelocity() const { return m_centroidVelocity; }
-
qreal minimumX() const { return m_minimumX; }
void setMinimumX(qreal minX);
qreal maximumX() const { return m_maximumX; }
@@ -135,10 +132,9 @@ protected:
private:
// properties
qreal m_activeScale;
+ qreal m_accumulatedScale;
qreal m_activeRotation;
QVector2D m_activeTranslation;
- QPointF m_centroid;
- QVector2D m_centroidVelocity;
qreal m_minimumScale;
qreal m_maximumScale;
@@ -156,7 +152,6 @@ private:
// internal
qreal m_startScale;
qreal m_startRotation;
- QPointF m_startCentroid;
qreal m_startDistance;
QPointF m_startPos;
diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp
index 06831613b6..7b9f6839b1 100644
--- a/src/quick/handlers/qquickpointerdevicehandler.cpp
+++ b/src/quick/handlers/qquickpointerdevicehandler.cpp
@@ -37,7 +37,7 @@
**
****************************************************************************/
-#include "qquickpointerdevicehandler_p.h"
+#include "qquickpointerdevicehandler_p_p.h"
#include <private/qquickitem_p.h>
#include <QMouseEvent>
#include <QDebug>
@@ -59,10 +59,12 @@ QT_BEGIN_NAMESPACE
allow filtering based on device type, pointer type, or keyboard modifiers.
*/
QQuickPointerDeviceHandler::QQuickPointerDeviceHandler(QObject *parent)
- : QQuickPointerHandler(parent)
- , m_acceptedDevices(QQuickPointerDevice::AllDevices)
- , m_acceptedPointerTypes(QQuickPointerDevice::AllPointerTypes)
- , m_acceptedModifiers(Qt::KeyboardModifierMask)
+ : QQuickPointerHandler(*(new QQuickPointerDeviceHandlerPrivate), parent)
+{
+}
+
+QQuickPointerDeviceHandler::QQuickPointerDeviceHandler(QQuickPointerDeviceHandlerPrivate &dd, QObject *parent)
+ : QQuickPointerHandler(dd, parent)
{
}
@@ -70,6 +72,69 @@ QQuickPointerDeviceHandler::~QQuickPointerDeviceHandler()
{
}
+QQuickPointerDevice::DeviceTypes QQuickPointerDeviceHandler::acceptedDevices() const
+{
+ Q_D(const QQuickPointerDeviceHandler);
+ return d->acceptedDevices;
+}
+
+QQuickPointerDevice::PointerTypes QQuickPointerDeviceHandler::acceptedPointerTypes() const
+{
+ Q_D(const QQuickPointerDeviceHandler);
+ return d->acceptedPointerTypes;
+}
+
+/*!
+ \qmlproperty int QtQuick::PointerDeviceHandler::acceptedButtons
+
+ The mouse buttons which can activate this Pointer Handler.
+
+ By default, this property is set to \l {QtQuick::MouseEvent::button} {Qt.LeftButton}.
+ It can be set to an OR combination of mouse buttons, and will ignore events
+ from other buttons.
+
+ For example, a control could be made to respond to left and right clicks
+ in different ways, with two handlers:
+
+ \qml
+ Item {
+ TapHandler {
+ onTapped: console.log("left clicked")
+ }
+ TapHandler {
+ acceptedButtons: Qt.RightButton
+ onTapped: console.log("right clicked")
+ }
+ }
+ \endqml
+
+ \note Tapping on a touchscreen or tapping the stylus on a graphics tablet
+ emulates clicking the left mouse button. This behavior can be altered via
+ \l {PointerDeviceHandler::acceptedDevices}{acceptedDevices} or
+ \l {PointerDeviceHandler::acceptedPointerTypes}{acceptedPointerTypes}.
+*/
+Qt::MouseButtons QQuickPointerDeviceHandler::acceptedButtons() const
+{
+ Q_D(const QQuickPointerDeviceHandler);
+ return d->acceptedButtons;
+}
+
+void QQuickPointerDeviceHandler::setAcceptedButtons(Qt::MouseButtons buttons)
+{
+ Q_D(QQuickPointerDeviceHandler);
+ if (d->acceptedButtons == buttons)
+ return;
+
+ d->acceptedButtons = buttons;
+ emit acceptedButtonsChanged();
+}
+
+Qt::KeyboardModifiers QQuickPointerDeviceHandler::acceptedModifiers() const
+{
+ Q_D(const QQuickPointerDeviceHandler);
+ return d->acceptedModifiers;
+}
+
/*!
\qmlproperty int PointerDeviceHandler::acceptedDevices
@@ -98,10 +163,11 @@ QQuickPointerDeviceHandler::~QQuickPointerDeviceHandler()
*/
void QQuickPointerDeviceHandler::setAcceptedDevices(QQuickPointerDevice::DeviceTypes acceptedDevices)
{
- if (m_acceptedDevices == acceptedDevices)
+ Q_D(QQuickPointerDeviceHandler);
+ if (d->acceptedDevices == acceptedDevices)
return;
- m_acceptedDevices = acceptedDevices;
+ d->acceptedDevices = acceptedDevices;
emit acceptedDevicesChanged();
}
@@ -136,10 +202,11 @@ void QQuickPointerDeviceHandler::setAcceptedDevices(QQuickPointerDevice::DeviceT
*/
void QQuickPointerDeviceHandler::setAcceptedPointerTypes(QQuickPointerDevice::PointerTypes acceptedPointerTypes)
{
- if (m_acceptedPointerTypes == acceptedPointerTypes)
+ Q_D(QQuickPointerDeviceHandler);
+ if (d->acceptedPointerTypes == acceptedPointerTypes)
return;
- m_acceptedPointerTypes = acceptedPointerTypes;
+ d->acceptedPointerTypes = acceptedPointerTypes;
emit acceptedPointerTypesChanged();
}
@@ -171,26 +238,28 @@ void QQuickPointerDeviceHandler::setAcceptedPointerTypes(QQuickPointerDevice::Po
*/
void QQuickPointerDeviceHandler::setAcceptedModifiers(Qt::KeyboardModifiers acceptedModifiers)
{
- if (m_acceptedModifiers == acceptedModifiers)
+ Q_D(QQuickPointerDeviceHandler);
+ if (d->acceptedModifiers == acceptedModifiers)
return;
- m_acceptedModifiers = acceptedModifiers;
+ d->acceptedModifiers = acceptedModifiers;
emit acceptedModifiersChanged();
}
bool QQuickPointerDeviceHandler::wantsPointerEvent(QQuickPointerEvent *event)
{
+ Q_D(QQuickPointerDeviceHandler);
if (!QQuickPointerHandler::wantsPointerEvent(event))
return false;
qCDebug(lcPointerHandlerDispatch) << objectName()
- << "checking device type" << m_acceptedDevices
- << "pointer type" << m_acceptedPointerTypes
- << "modifiers" << m_acceptedModifiers;
- if ((event->device()->type() & m_acceptedDevices) == 0)
+ << "checking device type" << d->acceptedDevices
+ << "pointer type" << d->acceptedPointerTypes
+ << "modifiers" << d->acceptedModifiers;
+ if ((event->device()->type() & d->acceptedDevices) == 0)
return false;
- if ((event->device()->pointerType() & m_acceptedPointerTypes) == 0)
+ if ((event->device()->pointerType() & d->acceptedPointerTypes) == 0)
return false;
- if (m_acceptedModifiers != Qt::KeyboardModifierMask && event->modifiers() != m_acceptedModifiers)
+ if (d->acceptedModifiers != Qt::KeyboardModifierMask && event->modifiers() != d->acceptedModifiers)
return false;
return true;
}
diff --git a/src/quick/handlers/qquickpointerdevicehandler_p.h b/src/quick/handlers/qquickpointerdevicehandler_p.h
index 1638604ea7..4194769fd7 100644
--- a/src/quick/handlers/qquickpointerdevicehandler_p.h
+++ b/src/quick/handlers/qquickpointerdevicehandler_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -36,6 +36,7 @@
** $QT_END_LICENSE$
**
****************************************************************************/
+#include "qquickpointerhandler_p.h"
#ifndef QQUICKPOINTERDEVICEHANDLER_H
#define QQUICKPOINTERDEVICEHANDLER_H
@@ -51,42 +52,45 @@
// We mean it.
//
-#include "qquickpointerhandler_p.h"
-
QT_BEGIN_NAMESPACE
+class QQuickPointerDeviceHandlerPrivate;
+
class Q_AUTOTEST_EXPORT QQuickPointerDeviceHandler : public QQuickPointerHandler
{
Q_OBJECT
Q_PROPERTY(QQuickPointerDevice::DeviceTypes acceptedDevices READ acceptedDevices WRITE setAcceptedDevices NOTIFY acceptedDevicesChanged)
Q_PROPERTY(QQuickPointerDevice::PointerTypes acceptedPointerTypes READ acceptedPointerTypes WRITE setAcceptedPointerTypes NOTIFY acceptedPointerTypesChanged)
+ Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged)
Q_PROPERTY(Qt::KeyboardModifiers acceptedModifiers READ acceptedModifiers WRITE setAcceptedModifiers NOTIFY acceptedModifiersChanged)
public:
explicit QQuickPointerDeviceHandler(QObject *parent = nullptr);
~QQuickPointerDeviceHandler();
- QQuickPointerDevice::DeviceTypes acceptedDevices() const { return m_acceptedDevices; }
- QQuickPointerDevice::PointerTypes acceptedPointerTypes() const { return m_acceptedPointerTypes; }
- Qt::KeyboardModifiers acceptedModifiers() const { return m_acceptedModifiers; }
+ QQuickPointerDevice::DeviceTypes acceptedDevices() const;
+ QQuickPointerDevice::PointerTypes acceptedPointerTypes() const;
+ Qt::MouseButtons acceptedButtons() const;
+ Qt::KeyboardModifiers acceptedModifiers() const;
-public slots:
+public Q_SLOTS:
void setAcceptedDevices(QQuickPointerDevice::DeviceTypes acceptedDevices);
void setAcceptedPointerTypes(QQuickPointerDevice::PointerTypes acceptedPointerTypes);
+ void setAcceptedButtons(Qt::MouseButtons buttons);
void setAcceptedModifiers(Qt::KeyboardModifiers acceptedModifiers);
Q_SIGNALS:
void acceptedDevicesChanged();
void acceptedPointerTypesChanged();
+ void acceptedButtonsChanged();
void acceptedModifiersChanged();
protected:
+ QQuickPointerDeviceHandler(QQuickPointerDeviceHandlerPrivate &dd, QObject *parent = nullptr);
+
bool wantsPointerEvent(QQuickPointerEvent *event) override;
-protected:
- QQuickPointerDevice::DeviceTypes m_acceptedDevices;
- QQuickPointerDevice::PointerTypes m_acceptedPointerTypes;
- Qt::KeyboardModifiers m_acceptedModifiers;
+ Q_DECLARE_PRIVATE(QQuickPointerDeviceHandler)
};
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointerdevicehandler_p_p.h b/src/quick/handlers/qquickpointerdevicehandler_p_p.h
new file mode 100644
index 0000000000..6a950590f3
--- /dev/null
+++ b/src/quick/handlers/qquickpointerdevicehandler_p_p.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPOINTERDEVICEHANDLER_P_H
+#define QQUICKPOINTERDEVICEHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickpointerdevicehandler_p.h"
+#include "qquickpointerhandler_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickPointerDeviceHandlerPrivate : public QQuickPointerHandlerPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickPointerDeviceHandler)
+
+public:
+ static QQuickPointerDeviceHandlerPrivate* get(QQuickPointerDeviceHandler *q) { return q->d_func(); }
+ static const QQuickPointerDeviceHandlerPrivate* get(const QQuickPointerDeviceHandler *q) { return q->d_func(); }
+
+ QQuickPointerDevice::DeviceTypes acceptedDevices = QQuickPointerDevice::AllDevices;
+ QQuickPointerDevice::PointerTypes acceptedPointerTypes = QQuickPointerDevice::AllPointerTypes;
+ Qt::MouseButtons acceptedButtons = Qt::LeftButton;
+ Qt::KeyboardModifiers acceptedModifiers = Qt::KeyboardModifierMask;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPOINTERDEVICEHANDLER_P_H
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp
index e5b1dc8985..8e10003c6a 100644
--- a/src/quick/handlers/qquickpointerhandler.cpp
+++ b/src/quick/handlers/qquickpointerhandler.cpp
@@ -38,6 +38,8 @@
****************************************************************************/
#include "qquickpointerhandler_p.h"
+#include "qquickpointerhandler_p_p.h"
+#include <QtQuick/private/qquickitem_p.h>
QT_BEGIN_NAMESPACE
@@ -55,20 +57,17 @@ Q_LOGGING_CATEGORY(lcPointerHandlerActive, "qt.quick.handler.active")
\ingroup qtquick-handlers
\brief Abstract handler for pointer events.
- PointerHandler is the base class handler (not registered as a QML type) for
- pointer events without regard to source (touch, mouse or graphics tablet).
+ PointerHandler is the base class Input Handler (not registered as a QML type) for
+ events from any kind of pointing device (touch, mouse or graphics tablet).
*/
QQuickPointerHandler::QQuickPointerHandler(QObject *parent)
- : QObject(parent)
- , m_currentEvent(nullptr)
- , m_target(nullptr)
- , m_enabled(true)
- , m_active(false)
- , m_targetExplicitlySet(false)
- , m_hadKeepMouseGrab(false)
- , m_hadKeepTouchGrab(false)
- , m_grabPermissions(CanTakeOverFromItems | CanTakeOverFromHandlersOfDifferentType | ApprovesTakeOverByAnything)
+ : QObject(*(new QQuickPointerHandlerPrivate), parent)
+{
+}
+
+QQuickPointerHandler::QQuickPointerHandler(QQuickPointerHandlerPrivate &dd, QObject *parent)
+ : QObject(dd, parent)
{
}
@@ -82,13 +81,46 @@ QQuickPointerHandler::~QQuickPointerHandler()
}
/*!
+ \qmlproperty real PointerHandler::margin
+
+ The margin beyond the bounds of the \l {PointerHandler::parent}{parent}
+ item within which an event point can activate this handler. For example, on
+ a PinchHandler where the \l {PointerHandler::target}{target} is also the
+ \c parent, it's useful to set this to a distance at least half the width
+ of a typical user's finger, so that if the \c parent has been scaled down
+ to a very small size, the pinch gesture is still possible. Or, if a
+ TapHandler-based button is placed near the screen edge, it can be used
+ to comply with Fitts's Law: react to mouse clicks at the screen edge
+ even though the button is visually spaced away from the edge by a few pixels.
+
+ The default value is 0.
+
+ \image pointDistanceThreshold.png
+*/
+qreal QQuickPointerHandler::margin() const
+{
+ Q_D(const QQuickPointerHandler);
+ return d->m_margin;
+}
+
+void QQuickPointerHandler::setMargin(qreal pointDistanceThreshold)
+{
+ Q_D(QQuickPointerHandler);
+ if (d->m_margin == pointDistanceThreshold)
+ return;
+
+ d->m_margin = pointDistanceThreshold;
+ emit marginChanged();
+}
+
+/*!
Notification that the grab has changed in some way which is relevant to this handler.
- The \a grabber (subject) will be the PointerHandler whose state is changing,
+ The \a grabber (subject) will be the Input Handler whose state is changing,
or null if the state change regards an Item.
The \a stateChange (verb) tells what happened.
The \a point (object) is the point that was grabbed or ungrabbed.
EventPoint has the sole responsibility to call this function.
- The PointerHandler must react in whatever way is appropriate, and must
+ The Input Handler must react in whatever way is appropriate, and must
emit the relevant signals (for the benefit of QML code).
A subclass is allowed to override this virtual function, but must always
call its parent class's implementation in addition to (usually after)
@@ -113,8 +145,9 @@ void QQuickPointerHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEv
setActive(false);
point->setAccepted(false);
if (auto par = parentItem()) {
- par->setKeepMouseGrab(m_hadKeepMouseGrab);
- par->setKeepTouchGrab(m_hadKeepTouchGrab);
+ Q_D(const QQuickPointerHandler);
+ par->setKeepMouseGrab(d->hadKeepMouseGrab);
+ par->setKeepTouchGrab(d->hadKeepTouchGrab);
}
break;
case QQuickEventPoint::OverrideGrabPassive:
@@ -124,19 +157,17 @@ void QQuickPointerHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEv
}
if (wasCanceled)
emit canceled(point);
- else
- emit grabChanged(point);
+ emit grabChanged(stateChange, point);
}
}
/*!
- \internal
Acquire or give up a passive grab of the given \a point, according to the \a grab state.
- Unlike the exclusive grab, multiple PointerHandlers can have passive grabs
+ Unlike the exclusive grab, multiple Input Handlers can have passive grabs
simultaneously. This means that each of them will receive further events
- when the \a point moves, and when it is finally released. Typically a
- PointerHandler should acquire a passive grab as soon as a point is pressed,
+ when the \a point moves, and when it is finally released. Typically an
+ Input Handler should acquire a passive grab as soon as a point is pressed,
if the handler's constraints do not clearly rule out any interest in that
point. For example, DragHandler needs a passive grab in order to watch the
movement of a point to see whether it will be dragged past the drag
@@ -181,19 +212,20 @@ bool QQuickPointerHandler::canGrab(QQuickEventPoint *point)
*/
bool QQuickPointerHandler::approveGrabTransition(QQuickEventPoint *point, QObject *proposedGrabber)
{
+ Q_D(const QQuickPointerHandler);
bool allowed = false;
if (proposedGrabber == this) {
QObject* existingGrabber = point->exclusiveGrabber();
- allowed = (existingGrabber == nullptr) || ((m_grabPermissions & CanTakeOverFromAnything) == CanTakeOverFromAnything);
+ allowed = (existingGrabber == nullptr) || ((d->grabPermissions & CanTakeOverFromAnything) == CanTakeOverFromAnything);
if (existingGrabber) {
if (QQuickPointerHandler *existingPhGrabber = point->grabberPointerHandler()) {
- if (!allowed && (m_grabPermissions & CanTakeOverFromHandlersOfDifferentType) &&
+ if (!allowed && (d->grabPermissions & CanTakeOverFromHandlersOfDifferentType) &&
existingPhGrabber->metaObject()->className() != metaObject()->className())
allowed = true;
- if (!allowed && (m_grabPermissions & CanTakeOverFromHandlersOfSameType) &&
+ if (!allowed && (d->grabPermissions & CanTakeOverFromHandlersOfSameType) &&
existingPhGrabber->metaObject()->className() == metaObject()->className())
allowed = true;
- } else if ((m_grabPermissions & CanTakeOverFromItems)) {
+ } else if ((d->grabPermissions & CanTakeOverFromItems)) {
QQuickItem * existingItemGrabber = point->grabberItem();
if (existingItemGrabber && !((existingItemGrabber->keepMouseGrab() && point->pointerEvent()->asPointerMouseEvent()) ||
(existingItemGrabber->keepTouchGrab() && point->pointerEvent()->asPointerTouchEvent())))
@@ -203,18 +235,18 @@ bool QQuickPointerHandler::approveGrabTransition(QQuickEventPoint *point, QObjec
} else {
// proposedGrabber is different: that means this instance will lose its grab
if (proposedGrabber) {
- if ((m_grabPermissions & ApprovesTakeOverByAnything) == ApprovesTakeOverByAnything)
+ if ((d->grabPermissions & ApprovesTakeOverByAnything) == ApprovesTakeOverByAnything)
allowed = true;
- if (!allowed && (m_grabPermissions & ApprovesTakeOverByHandlersOfDifferentType) &&
+ if (!allowed && (d->grabPermissions & ApprovesTakeOverByHandlersOfDifferentType) &&
proposedGrabber->metaObject()->className() != metaObject()->className())
allowed = true;
- if (!allowed && (m_grabPermissions & ApprovesTakeOverByHandlersOfSameType) &&
+ if (!allowed && (d->grabPermissions & ApprovesTakeOverByHandlersOfSameType) &&
proposedGrabber->metaObject()->className() == metaObject()->className())
allowed = true;
- if (!allowed && (m_grabPermissions & ApprovesTakeOverByItems) && proposedGrabber->inherits("QQuickItem"))
+ if (!allowed && (d->grabPermissions & ApprovesTakeOverByItems) && proposedGrabber->inherits("QQuickItem"))
allowed = true;
} else {
- if (!allowed && (m_grabPermissions & ApprovesCancellation))
+ if (!allowed && (d->grabPermissions & ApprovesCancellation))
allowed = true;
}
}
@@ -236,12 +268,19 @@ bool QQuickPointerHandler::approveGrabTransition(QQuickEventPoint *point, QObjec
which allows most takeover scenarios but avoids e.g. two PinchHandlers fighting
over the same touchpoints.
*/
+QQuickPointerHandler::GrabPermissions QQuickPointerHandler::grabPermissions() const
+{
+ Q_D(const QQuickPointerHandler);
+ return static_cast<QQuickPointerHandler::GrabPermissions>(d->grabPermissions);
+}
+
void QQuickPointerHandler::setGrabPermissions(GrabPermissions grabPermission)
{
- if (m_grabPermissions == grabPermission)
+ Q_D(QQuickPointerHandler);
+ if (d->grabPermissions == grabPermission)
return;
- m_grabPermissions = grabPermission;
+ d->grabPermissions = grabPermission;
emit grabPermissionChanged();
}
@@ -253,8 +292,13 @@ void QQuickPointerHandler::componentComplete()
{
}
+QQuickPointerEvent *QQuickPointerHandler::currentEvent()
+{
+ Q_D(const QQuickPointerHandler);
+ return d->currentEvent;
+}
+
/*!
- \internal
Acquire or give up the exclusive grab of the given \a point, according to
the \a grab state, and subject to the rules: canGrab(), and the rule not to
relinquish another handler's grab. Returns true if permission is granted,
@@ -284,7 +328,6 @@ bool QQuickPointerHandler::setExclusiveGrab(QQuickEventPoint *point, bool grab)
}
/*!
- \internal
Cancel any existing grab of the given \a point.
*/
void QQuickPointerHandler::cancelAllGrabs(QQuickEventPoint *point)
@@ -300,9 +343,19 @@ QPointF QQuickPointerHandler::eventPos(const QQuickEventPoint *point) const
bool QQuickPointerHandler::parentContains(const QQuickEventPoint *point) const
{
- if (point) {
- if (QQuickItem *par = parentItem())
- return par->contains(par->mapFromScene(point->scenePosition()));
+ if (!point)
+ return false;
+ if (QQuickItem *par = parentItem()) {
+ if (par->window()) {
+ QPoint screenPosition = par->window()->mapToGlobal(point->scenePosition().toPoint());
+ if (!par->window()->geometry().contains(screenPosition))
+ return false;
+ }
+ QPointF p = par->mapFromScene(point->scenePosition());
+ qreal m = margin();
+ if (m > 0)
+ return p.x() >= -m && p.y() >= -m && p.x() <= par->width() + m && p.y() <= par->height() + m;
+ return par->contains(p);
}
return false;
}
@@ -313,15 +366,28 @@ bool QQuickPointerHandler::parentContains(const QQuickEventPoint *point) const
If a PointerHandler is disabled, it will reject all events
and no signals will be emitted.
*/
+bool QQuickPointerHandler::enabled() const
+{
+ Q_D(const QQuickPointerHandler);
+ return d->enabled;
+}
+
void QQuickPointerHandler::setEnabled(bool enabled)
{
- if (m_enabled == enabled)
+ Q_D(QQuickPointerHandler);
+ if (d->enabled == enabled)
return;
- m_enabled = enabled;
+ d->enabled = enabled;
emit enabledChanged();
}
+bool QQuickPointerHandler::active() const
+{
+ Q_D(const QQuickPointerHandler);
+ return d->active;
+}
+
/*!
\qmlproperty Item QtQuick::PointerHandler::target
@@ -335,21 +401,28 @@ void QQuickPointerHandler::setEnabled(bool enabled)
*/
void QQuickPointerHandler::setTarget(QQuickItem *target)
{
- m_targetExplicitlySet = true;
- if (m_target == target)
+ Q_D(QQuickPointerHandler);
+ d->targetExplicitlySet = true;
+ if (d->target == target)
return;
- QQuickItem *oldTarget = m_target;
- m_target = target;
+ QQuickItem *oldTarget = d->target;
+ d->target = target;
onTargetChanged(oldTarget);
emit targetChanged();
}
+QQuickItem *QQuickPointerHandler::parentItem() const
+{
+ return static_cast<QQuickItem *>(QObject::parent());
+}
+
QQuickItem *QQuickPointerHandler::target() const
{
- if (!m_targetExplicitlySet)
+ Q_D(const QQuickPointerHandler);
+ if (!d->targetExplicitlySet)
return parentItem();
- return m_target;
+ return d->target;
}
void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event)
@@ -374,15 +447,24 @@ void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event)
bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event)
{
+ Q_D(const QQuickPointerHandler);
Q_UNUSED(event)
- return m_enabled;
+ return d->enabled;
+}
+
+bool QQuickPointerHandler::wantsEventPoint(QQuickEventPoint *point)
+{
+ bool ret = parentContains(point);
+ qCDebug(lcPointerHandlerDispatch) << hex << point->pointId() << "@" << point->scenePosition()
+ << metaObject()->className() << objectName() << ret;
+ return ret;
}
/*!
\readonly
\qmlproperty bool QtQuick::PointerHandler::active
- This holds true whenever this PointerHandler has taken sole responsibility
+ This holds true whenever this Input Handler has taken sole responsibility
for handing one or more EventPoints, by successfully taking an exclusive
grab of those points. This means that it is keeping its properties
up-to-date according to the movements of those Event Points and actively
@@ -390,9 +472,10 @@ bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event)
*/
void QQuickPointerHandler::setActive(bool active)
{
- if (m_active != active) {
- qCDebug(lcPointerHandlerActive) << this << m_active << "->" << active;
- m_active = active;
+ Q_D(QQuickPointerHandler);
+ if (d->active != active) {
+ qCDebug(lcPointerHandlerActive) << this << d->active << "->" << active;
+ d->active = active;
onActiveChanged();
emit activeChanged();
}
@@ -400,7 +483,8 @@ void QQuickPointerHandler::setActive(bool active)
void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event)
{
- m_currentEvent = event;
+ Q_D(QQuickPointerHandler);
+ d->currentEvent = event;
}
/*!
@@ -417,10 +501,13 @@ void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event)
*/
/*!
- \qmlsignal QtQuick::PointerHandler::grabChanged(EventPoint point)
+ \qmlsignal QtQuick::PointerHandler::grabChanged(GrabState stateChange, EventPoint point)
+
+ This signal is emitted when the grab has changed in some way which is
+ relevant to this handler.
- This signal is emitted when this handler has acquired or relinquished a
- passive or exclusive grab of the given \a point.
+ The \a stateChange (verb) tells what happened.
+ The \a point (object) is the point that was grabbed or ungrabbed.
*/
/*!
@@ -430,4 +517,20 @@ void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event)
emitted when the grab is stolen by a different Pointer Handler or Item.
*/
+QQuickPointerHandlerPrivate::QQuickPointerHandlerPrivate()
+ : grabPermissions(QQuickPointerHandler::CanTakeOverFromItems |
+ QQuickPointerHandler::CanTakeOverFromHandlersOfDifferentType |
+ QQuickPointerHandler::ApprovesTakeOverByAnything)
+ , enabled(true)
+ , active(false)
+ , targetExplicitlySet(false)
+ , hadKeepMouseGrab(false)
+ , hadKeepTouchGrab(false)
+{
+}
+
+QQuickPointerHandlerPrivate::~QQuickPointerHandlerPrivate()
+{
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h
index e2bcce8fc9..9644bb959f 100644
--- a/src/quick/handlers/qquickpointerhandler_p.h
+++ b/src/quick/handlers/qquickpointerhandler_p.h
@@ -51,8 +51,6 @@
// We mean it.
//
-#include "qevent.h"
-
#include <QtQuick/private/qquickevents_p_p.h>
#include <QtQuick/private/qquickitem_p.h>
@@ -60,6 +58,8 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcPointerHandlerDispatch)
+class QQuickPointerHandlerPrivate;
+
class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandler : public QObject, public QQmlParserStatus
{
Q_OBJECT
@@ -70,6 +70,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandler : public QObject, public QQmlP
Q_PROPERTY(QQuickItem * target READ target WRITE setTarget NOTIFY targetChanged)
Q_PROPERTY(QQuickItem * parent READ parentItem CONSTANT)
Q_PROPERTY(GrabPermissions grabPermissions READ grabPermissions WRITE setGrabPermissions NOTIFY grabPermissionChanged)
+ Q_PROPERTY(qreal margin READ margin WRITE setMargin NOTIFY marginChanged)
public:
explicit QQuickPointerHandler(QObject *parent = nullptr);
@@ -91,35 +92,42 @@ public:
Q_FLAG(GrabPermissions)
public:
- bool enabled() const { return m_enabled; }
+ bool enabled() const;
void setEnabled(bool enabled);
- bool active() const { return m_active; }
+ bool active() const;
QQuickItem *target() const;
void setTarget(QQuickItem *target);
- QQuickItem * parentItem() const { return static_cast<QQuickItem *>(QObject::parent()); }
+ QQuickItem * parentItem() const;
void handlePointerEvent(QQuickPointerEvent *event);
- GrabPermissions grabPermissions() const { return static_cast<GrabPermissions>(m_grabPermissions); }
+ GrabPermissions grabPermissions() const;
void setGrabPermissions(GrabPermissions grabPermissions);
+ qreal margin() const;
+ void setMargin(qreal pointDistanceThreshold);
+
Q_SIGNALS:
void enabledChanged();
void activeChanged();
void targetChanged();
- void grabChanged(QQuickEventPoint *point);
+ void marginChanged();
+ void grabChanged(QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point);
void grabPermissionChanged();
void canceled(QQuickEventPoint *point);
protected:
+ QQuickPointerHandler(QQuickPointerHandlerPrivate &dd, QObject *parent);
+
void classBegin() override;
void componentComplete() override;
- QQuickPointerEvent *currentEvent() { return m_currentEvent; }
+ QQuickPointerEvent *currentEvent();
virtual bool wantsPointerEvent(QQuickPointerEvent *event);
+ virtual bool wantsEventPoint(QQuickEventPoint *point);
virtual void handlePointerEventImpl(QQuickPointerEvent *event);
void setActive(bool active);
virtual void onTargetChanged(QQuickItem *oldTarget) { Q_UNUSED(oldTarget); }
@@ -133,19 +141,11 @@ protected:
QPointF eventPos(const QQuickEventPoint *point) const;
bool parentContains(const QQuickEventPoint *point) const;
-private:
- QQuickPointerEvent *m_currentEvent;
- QQuickItem *m_target;
- bool m_enabled : 1;
- bool m_active : 1;
- bool m_targetExplicitlySet : 1;
- bool m_hadKeepMouseGrab : 1; // some handlers override target()->setKeepMouseGrab(); this remembers previous state
- bool m_hadKeepTouchGrab : 1; // some handlers override target()->setKeepTouchGrab(); this remembers previous state
- uint m_reserved : 19;
- uint8_t m_grabPermissions : 8;
-
friend class QQuickEventPoint;
+ friend class QQuickItemPrivate;
friend class QQuickWindowPrivate;
+
+ Q_DECLARE_PRIVATE(QQuickPointerHandler)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerHandler::GrabPermissions)
diff --git a/src/quick/handlers/qquickpointerhandler_p_p.h b/src/quick/handlers/qquickpointerhandler_p_p.h
new file mode 100644
index 0000000000..c0b2b84a66
--- /dev/null
+++ b/src/quick/handlers/qquickpointerhandler_p_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPOINTERHANDLER_P_H
+#define QQUICKPOINTERHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qevent.h"
+
+#include <QtQuick/private/qquickevents_p_p.h>
+#include <QtQuick/private/qquickpointerhandler_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandlerPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickPointerHandler)
+
+public:
+ static QQuickPointerHandlerPrivate* get(QQuickPointerHandler *q) { return q->d_func(); }
+ static const QQuickPointerHandlerPrivate* get(const QQuickPointerHandler *q) { return q->d_func(); }
+
+ QQuickPointerHandlerPrivate();
+ virtual ~QQuickPointerHandlerPrivate() override;
+
+ QQuickPointerEvent *currentEvent = nullptr;
+ QQuickItem *target = nullptr;
+ qreal m_margin = 0;
+ uint8_t grabPermissions : 8;
+ bool enabled : 1;
+ bool active : 1;
+ bool targetExplicitlySet : 1;
+ bool hadKeepMouseGrab : 1; // some handlers override target()->setKeepMouseGrab(); this remembers previous state
+ bool hadKeepTouchGrab : 1; // some handlers override target()->setKeepTouchGrab(); this remembers previous state
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPOINTERHANDLER_P_H
diff --git a/src/quick/handlers/qquickpointhandler.cpp b/src/quick/handlers/qquickpointhandler.cpp
index 7e2d40452c..7cf37969cc 100644
--- a/src/quick/handlers/qquickpointhandler.cpp
+++ b/src/quick/handlers/qquickpointhandler.cpp
@@ -97,7 +97,7 @@ QT_BEGIN_NAMESPACE
\snippet pointerHandlers/pointHandler.qml 0
- Like all pointer handlers, a PointHandler has a \l target property, which
+ Like all input handlers, a PointHandler has a \l target property, which
may be used as a convenient place to put a point-tracking Item; but
PointHandler will not automatically manipulate the \c target item in any way.
You need to use bindings to make it react to the \l point.
diff --git a/src/quick/handlers/qquicksinglepointhandler.cpp b/src/quick/handlers/qquicksinglepointhandler.cpp
index 9df20390e4..71c05891d4 100644
--- a/src/quick/handlers/qquicksinglepointhandler.cpp
+++ b/src/quick/handlers/qquicksinglepointhandler.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -61,7 +61,6 @@ Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH_TARGET)
QQuickSinglePointHandler::QQuickSinglePointHandler(QObject *parent)
: QQuickPointerDeviceHandler(parent)
- , m_acceptedButtons(Qt::LeftButton)
, m_ignoreAdditionalPoints(false)
{
}
@@ -71,7 +70,7 @@ bool QQuickSinglePointHandler::wantsPointerEvent(QQuickPointerEvent *event)
if (!QQuickPointerDeviceHandler::wantsPointerEvent(event))
return false;
if (event->device()->pointerType() != QQuickPointerDevice::Finger &&
- (event->buttons() & m_acceptedButtons) == 0 && (event->button() & m_acceptedButtons) == 0)
+ (event->buttons() & acceptedButtons()) == 0 && (event->button() & acceptedButtons()) == 0)
return false;
if (m_pointInfo.m_id) {
@@ -79,16 +78,23 @@ bool QQuickSinglePointHandler::wantsPointerEvent(QQuickPointerEvent *event)
// It's expected to be an update or a release.
// If we no longer want it, cancel the grab.
int candidatePointCount = 0;
+ bool missing = true;
QQuickEventPoint *point = nullptr;
int c = event->pointCount();
for (int i = 0; i < c; ++i) {
QQuickEventPoint *p = event->point(i);
+ const bool found = (p->pointId() == m_pointInfo.m_id);
+ if (found)
+ missing = false;
if (wantsEventPoint(p)) {
++candidatePointCount;
- if (p->pointId() == m_pointInfo.m_id)
+ if (found)
point = p;
}
}
+ if (missing)
+ qCWarning(DBG_TOUCH_TARGET) << this << "pointId" << hex << m_pointInfo.m_id
+ << "is missing from current event, but was neither canceled nor released";
if (point) {
if (candidatePointCount == 1 || (candidatePointCount > 1 && m_ignoreAdditionalPoints)) {
point->setAccepted();
@@ -97,8 +103,6 @@ bool QQuickSinglePointHandler::wantsPointerEvent(QQuickPointerEvent *event)
point->cancelAllGrabs(this);
}
} else {
- qCWarning(DBG_TOUCH_TARGET) << this << "pointId" << hex << m_pointInfo.m_id
- << "is missing from current event, but was neither canceled nor released";
return false;
}
} else {
@@ -115,7 +119,7 @@ bool QQuickSinglePointHandler::wantsPointerEvent(QQuickPointerEvent *event)
}
}
if (chosen && candidatePointCount == 1) {
- m_pointInfo.m_id = chosen->pointId();
+ setPointId(chosen->pointId());
chosen->setAccepted();
}
}
@@ -130,48 +134,16 @@ void QQuickSinglePointHandler::handlePointerEventImpl(QQuickPointerEvent *event)
if (!m_pointInfo.m_id || !currentPoint->isAccepted()) {
reset();
} else {
- if (event->asPointerTouchEvent()) {
- QQuickEventTouchPoint *tp = static_cast<QQuickEventTouchPoint *>(currentPoint);
- m_pointInfo.m_uniqueId = tp->uniqueId();
- m_pointInfo.m_rotation = tp->rotation();
- m_pointInfo.m_pressure = tp->pressure();
- m_pointInfo.m_ellipseDiameters = tp->ellipseDiameters();
- } else if (event->asPointerTabletEvent()) {
- // TODO
- } else {
- m_pointInfo.m_uniqueId = event->device()->uniqueId();
- m_pointInfo.m_rotation = 0;
- m_pointInfo.m_pressure = event->buttons() ? 1 : 0;
- m_pointInfo.m_ellipseDiameters = QSizeF();
- }
- m_pointInfo.m_position = currentPoint->position();
- m_pointInfo.m_scenePosition = currentPoint->scenePosition();
- if (currentPoint->state() == QQuickEventPoint::Updated)
- m_pointInfo.m_velocity = currentPoint->velocity();
+ m_pointInfo.reset(currentPoint);
handleEventPoint(currentPoint);
- switch (currentPoint->state()) {
- case QQuickEventPoint::Pressed:
- m_pointInfo.m_pressPosition = currentPoint->position();
- m_pointInfo.m_scenePressPosition = currentPoint->scenePosition();
- m_pointInfo.m_pressedButtons = event->buttons();
- break;
- case QQuickEventPoint::Released:
+ if (currentPoint->state() == QQuickEventPoint::Released) {
setExclusiveGrab(currentPoint, false);
reset();
- break;
- default:
- m_pointInfo.m_pressedButtons = event->buttons();
- break;
}
emit pointChanged();
}
}
-bool QQuickSinglePointHandler::wantsEventPoint(QQuickEventPoint *point)
-{
- return parentContains(point);
-}
-
void QQuickSinglePointHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point)
{
if (grabber != this)
@@ -212,42 +184,9 @@ void QQuickSinglePointHandler::moveTarget(QPointF pos, QQuickEventPoint *point)
m_pointInfo.m_position = target()->mapFromScene(m_pointInfo.m_scenePosition);
}
-/*!
- \qmlproperty int QtQuick::SinglePointHandler::acceptedButtons
-
- The mouse buttons which can activate this Pointer Handler.
-
- By default, this property is set to \l {QtQuick::MouseEvent::button} {Qt.LeftButton}.
- It can be set to an OR combination of mouse buttons, and will ignore events
- from other buttons.
-
- For example, a control could be made to respond to left and right clicks
- in different ways, with two handlers:
-
- \qml
- Item {
- TapHandler {
- onTapped: console.log("left clicked")
- }
- TapHandler {
- acceptedButtons: Qt.RightButton
- onTapped: console.log("right clicked")
- }
- }
- \endqml
-
- \note Tapping on a touchscreen or tapping the stylus on a graphics tablet
- emulates clicking the left mouse button. This behavior can be altered via
- \l {PointerDeviceHandler::acceptedDevices}{acceptedDevices} or
- \l {PointerDeviceHandler::acceptedPointerTypes}{acceptedPointerTypes}.
-*/
-void QQuickSinglePointHandler::setAcceptedButtons(Qt::MouseButtons buttons)
+void QQuickSinglePointHandler::setPointId(int id)
{
- if (m_acceptedButtons == buttons)
- return;
-
- m_acceptedButtons = buttons;
- emit acceptedButtonsChanged();
+ m_pointInfo.m_id = id;
}
void QQuickSinglePointHandler::reset()
@@ -264,213 +203,4 @@ void QQuickSinglePointHandler::reset()
handled, this object is reset to default values (all coordinates are 0).
*/
-/*!
- \qmltype HandlerPoint
- \instantiates QQuickHandlerPoint
- \inqmlmodule Qt.labs.handlers
- \ingroup qtquick-handlers
- \brief An event point.
-
- A QML representation of a QQuickEventPoint.
-
- It's possible to make bindings to properties of a \l SinglePointHandler's
- current point. For example:
-
- \snippet pointerHandlers/dragHandlerNullTarget.qml 0
-
- The point is kept up-to-date when the DragHandler is actively responding to
- an EventPoint; but when the point is released, or the current point is
- being handled by a different handler, \c position.x and \c position.y are 0.
-
- \note This is practically identical to QtQuick::EventPoint; however an
- EventPoint is a long-lived QObject which is invalidated between gestures
- and reused for subsequent event deliveries. Continuous bindings to its
- properties are not possible, and an individual handler cannot rely on it
- outside the period when that point is part of an active gesture which that
- handler is handling. HandlerPoint is a Q_GADGET that the handler owns.
- This allows you to make lifetime bindings to its properties.
-
- \sa SinglePointHandler::point
-*/
-
-QQuickHandlerPoint::QQuickHandlerPoint()
- : m_id(0)
- , m_rotation(0)
- , m_pressure(0)
-{}
-
-void QQuickHandlerPoint::reset()
-{
- m_id = 0;
- m_uniqueId = QPointingDeviceUniqueId();
- m_position = QPointF();
- m_scenePosition = QPointF();
- m_pressPosition = QPointF();
- m_scenePressPosition = QPointF();
- m_sceneGrabPosition = QPointF();
- m_velocity = QVector2D();
- m_rotation = 0;
- m_pressure = 0;
- m_ellipseDiameters = QSizeF();
- m_pressedButtons = Qt::NoButton;
-}
-
-/*!
- \readonly
- \qmlproperty int QtQuick::HandlerPoint::id
- \brief The ID number of the point
-
- During a touch gesture, from the time that the first finger is pressed
- until the last finger is released, each touchpoint will have a unique ID
- number. Likewise, if input from multiple devices occurs (for example
- simultaneous mouse and touch presses), all the current event points from
- all the devices will have unique IDs.
-
- \note Do not assume that id numbers start at zero or that they are
- sequential. Such an assumption is often false due to the way the underlying
- drivers work.
-
- \sa QTouchEvent::TouchPoint::id
-*/
-
-/*!
- \readonly
- \qmlproperty PointingDeviceUniqueId QtQuick::HandlerPoint::uniqueId
- \brief The unique ID of the point, if any
-
- This is normally empty, because touchscreens cannot uniquely identify fingers.
-
- On some types of touchscreens, especially those using TUIO drivers,
- it's possible to use recognizable physical tokens (fiducial objects)
- in addition to fingers. So if this point is a touch point, and
- uniqueId is set, it is the identifier for such an object.
-
- On a graphics tablet, each type of stylus or other tool often has a unique
- ID or serial number, which can be useful to respond in different ways to
- different tools.
-
- Interpreting the contents of this ID requires knowledge of the hardware and
- drivers in use.
-
- \sa QTabletEvent::uniqueId, QtQuick::TouchPoint::uniqueId, QtQuick::EventTouchPoint::uniqueId
-*/
-
-/*!
- \readonly
- \qmlproperty QPointF QtQuick::HandlerPoint::position
- \brief The position within the \c parent Item
-
- This is the position of the event point relative to the bounds of
- the \l {PointerHandler::parent} {parent}.
-*/
-
-/*!
- \readonly
- \qmlproperty QPointF QtQuick::HandlerPoint::scenePosition
- \brief The position within the scene
-
- This is the position of the event point relative to the bounds of the Qt
- Quick scene (typically the whole window).
-*/
-
-/*!
- \readonly
- \qmlproperty QPointF QtQuick::HandlerPoint::pressPosition
- \brief The pressed position within the \c parent Item
-
- This is the position at which this point was pressed, relative to the
- bounds of the \l {PointerHandler::parent} {parent}.
-*/
-
-/*!
- \readonly
- \qmlproperty QPointF QtQuick::HandlerPoint::scenePressPosition
- \brief The pressed position within the scene
-
- This is the position at which this point was pressed, in the coordinate
- system of the \l {Qt Quick Scene Graph}{scene graph}.
-*/
-
-/*!
- \readonly
- \qmlproperty QPointF QtQuick::HandlerPoint::sceneGrabPosition
- \brief The grabbed position within the scene
-
- If this point has been grabbed by a Pointer Handler or an Item, it means
- that object has taken sole responsibility for handling the movement and the
- release if this point. In that case, this is the position at which the grab
- occurred, in the coordinate system of the \l {Qt Quick Scene Graph}{scene graph}.
-*/
-
-/*!
- \readonly
- \qmlproperty enum QtQuick::HandlerPoint::pressedButtons
- \brief Which mouse or stylus buttons are currently pressed
-
- \sa MouseArea::pressedButtons
-*/
-
-/*!
- \readonly
- \qmlproperty QVector2D QtQuick::HandlerPoint::velocity
- \brief A vector representing the average speed and direction of movement
-
- This is a velocity vector pointing in the direction of movement, in logical
- pixels per second. It has x and y components, at least one of which will be
- nonzero when this point is in motion. It holds the average recent velocity:
- how fast and in which direction the event point has been moving recently.
-
- \sa QtQuick::EventPoint::velocity, QtQuick::TouchPoint::velocity, QTouchEvent::TouchPoint::velocity
-*/
-
-/*!
- \readonly
- \qmlproperty qreal QtQuick::HandlerPoint::rotation
-
- This property holds the rotation angle of the stylus on a graphics tablet
- or the contact patch of a touchpoint on a touchscreen.
-
- It is valid only with certain tablet stylus devices and touchscreens that
- can measure the rotation angle. Otherwise, it will be zero.
-*/
-
-/*!
- \readonly
- \qmlproperty qreal QtQuick::HandlerPoint::pressure
-
- This property tells how hard the user is pressing the stylus on a graphics
- tablet or the finger against a touchscreen, in the range from \c 0 (no
- measurable pressure) to \c 1.0 (maximum pressure which the device can
- measure).
-
- It is valid only with certain tablets and touchscreens that can measure
- pressure. Otherwise, it will be zero.
-*/
-
-/*!
- \readonly
- \qmlproperty size QtQuick::HandlerPoint::ellipseDiameters
-
- This property holds the diameters of the contact patch, if the event
- comes from a touchpoint and the device provides this information.
-
- A touchpoint is modeled as an elliptical area where the finger is pressed
- against the touchscreen. (In fact, it could also be modeled as a bitmap;
- but in that case we expect an elliptical bounding estimate to be fitted to
- the contact patch before the event is sent.) The harder the user presses,
- the larger the contact patch; so, these diameters provide an alternate way
- of detecting pressure, in case the device does not include a separate
- pressure sensor. The ellipse is centered on \l scenePosition (\l position
- in the PointerHandler's Item's local coordinates). The \l rotation property
- provides the rotation of the ellipse, if known. It is expected that if the
- \l rotation is zero, the \l {QSize::height}{height} is the larger dimension
- (the major axis), because of the usual hand position, reaching upward or
- outward across the surface.
-
- If the contact patch is unknown, or the device is not a touchscreen,
- these values will be zero.
-
- \sa QtQuick::EventTouchPoint::ellipseDiameters, QtQuick::TouchPoint::ellipseDiameters, QTouchEvent::TouchPoint::ellipseDiameters
-*/
-
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquicksinglepointhandler_p.h b/src/quick/handlers/qquicksinglepointhandler_p.h
index 7606b4f7ba..7c225aab46 100644
--- a/src/quick/handlers/qquicksinglepointhandler_p.h
+++ b/src/quick/handlers/qquicksinglepointhandler_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -51,82 +51,27 @@
// We mean it.
//
+#include "qquickhandlerpoint_p.h"
#include "qquickpointerdevicehandler_p.h"
QT_BEGIN_NAMESPACE
-class QQuickSinglePointHandler;
-
-class Q_QUICK_PRIVATE_EXPORT QQuickHandlerPoint {
- Q_GADGET
- Q_PROPERTY(int id READ id)
- Q_PROPERTY(QPointingDeviceUniqueId uniqueId READ uniqueId)
- Q_PROPERTY(QPointF position READ position)
- Q_PROPERTY(QPointF scenePosition READ scenePosition)
- Q_PROPERTY(QPointF pressPosition READ pressPosition)
- Q_PROPERTY(QPointF scenePressPosition READ scenePressPosition)
- Q_PROPERTY(QPointF sceneGrabPosition READ sceneGrabPosition)
- Q_PROPERTY(Qt::MouseButtons pressedButtons READ pressedButtons)
- Q_PROPERTY(QVector2D velocity READ velocity)
- Q_PROPERTY(qreal rotation READ rotation)
- Q_PROPERTY(qreal pressure READ pressure)
- Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters)
-
-public:
- QQuickHandlerPoint();
-
- int id() const { return m_id; }
- Qt::MouseButtons pressedButtons() const { return m_pressedButtons; }
- QPointF pressPosition() const { return m_pressPosition; }
- QPointF scenePressPosition() const { return m_scenePressPosition; }
- QPointF sceneGrabPosition() const { return m_sceneGrabPosition; }
- QPointF position() const { return m_position; }
- QPointF scenePosition() const { return m_scenePosition; }
- QVector2D velocity() const { return m_velocity; }
- qreal rotation() const { return m_rotation; }
- qreal pressure() const { return m_pressure; }
- QSizeF ellipseDiameters() const { return m_ellipseDiameters; }
- QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; }
-
-private:
- void reset();
- int m_id;
- QPointingDeviceUniqueId m_uniqueId;
- Qt::MouseButtons m_pressedButtons;
- QPointF m_position;
- QPointF m_scenePosition;
- QPointF m_pressPosition;
- QPointF m_scenePressPosition;
- QPointF m_sceneGrabPosition;
- QVector2D m_velocity;
- qreal m_rotation;
- qreal m_pressure;
- QSizeF m_ellipseDiameters;
- friend class QQuickSinglePointHandler;
-};
-
class Q_QUICK_PRIVATE_EXPORT QQuickSinglePointHandler : public QQuickPointerDeviceHandler
{
Q_OBJECT
- Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged)
Q_PROPERTY(QQuickHandlerPoint point READ point NOTIFY pointChanged)
public:
explicit QQuickSinglePointHandler(QObject *parent = nullptr);
virtual ~QQuickSinglePointHandler() { }
- Qt::MouseButtons acceptedButtons() const { return m_acceptedButtons; }
- void setAcceptedButtons(Qt::MouseButtons buttons);
-
QQuickHandlerPoint point() const { return m_pointInfo; }
Q_SIGNALS:
void pointChanged();
void singlePointGrabChanged(); // QQuickPointerHandler::grabChanged signal can't be a property notifier here
- void acceptedButtonsChanged();
protected:
bool wantsPointerEvent(QQuickPointerEvent *event) override;
- virtual bool wantsEventPoint(QQuickEventPoint *point);
void handlePointerEventImpl(QQuickPointerEvent *event) override;
virtual void handleEventPoint(QQuickEventPoint *point) = 0;
@@ -137,18 +82,18 @@ protected:
void moveTarget(QPointF pos, QQuickEventPoint *point);
+ void setPointId(int id);
+
private:
void reset();
private:
QQuickHandlerPoint m_pointInfo;
- Qt::MouseButtons m_acceptedButtons;
bool m_ignoreAdditionalPoints : 1;
};
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickHandlerPoint)
QML_DECLARE_TYPE(QQuickSinglePointHandler)
#endif // QQUICKPOINTERSINGLEHANDLER_H
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
index 902ff0df10..255059b9bd 100644
--- a/src/quick/handlers/qquicktaphandler.cpp
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -65,7 +65,7 @@ int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1);
value is DragThreshold, which requires the press and release to be close
together in both space and time. In this case, DragHandler is able to
function using only a passive grab, and therefore does not interfere with
- event delivery to any other Items or Pointer Handlers. So the default
+ event delivery to any other Items or Input Handlers. So the default
gesturePolicy is useful when you want to modify behavior of an existing
control or Item by adding a TapHandler with bindings and/or JavaScript
callbacks.
@@ -107,7 +107,7 @@ QQuickTapHandler::~QQuickTapHandler()
{
}
-static bool dragOverThreshold(QQuickEventPoint *point)
+static bool dragOverThreshold(const QQuickEventPoint *point)
{
QPointF delta = point->scenePosition() - point->scenePressPosition();
return (QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point) ||
@@ -237,7 +237,7 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event)
threshold (QStyleHints::startDragDistance), the tap gesture is
canceled, even if the button or finger is still pressed. This policy
can be useful whenever TapHandler needs to cooperate with other
- pointer handlers (for example \l DragHandler) or event-handling Items
+ input handlers (for example \l DragHandler) or event-handling Items
(for example QtQuick Controls), because in this case TapHandler
will not take the exclusive grab, but merely a passive grab.
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index 2c115f4d3c..e4012941cd 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -563,6 +563,8 @@ struct QQuickJSContext2D : public QV4::Object
static QV4::ReturnedValue method_set_lineWidth(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_get_miterLimit(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_set_miterLimit(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
+ static QV4::ReturnedValue method_set_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
+ static QV4::ReturnedValue method_get_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_get_shadowBlur(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_set_shadowBlur(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
@@ -596,7 +598,7 @@ public:
static QV4::Heap::QQuickJSContext2DPrototype *create(QV4::ExecutionEngine *engine)
{
QV4::Scope scope(engine);
- QV4::Scoped<QQuickJSContext2DPrototype> o(scope, engine->memoryManager->allocObject<QQuickJSContext2DPrototype>());
+ QV4::Scoped<QQuickJSContext2DPrototype> o(scope, engine->memoryManager->allocate<QQuickJSContext2DPrototype>());
o->defineDefaultProperty(QStringLiteral("quadraticCurveTo"), method_quadraticCurveTo, 0);
o->defineDefaultProperty(QStringLiteral("restore"), method_restore, 0);
@@ -641,6 +643,8 @@ public:
o->defineDefaultProperty(QStringLiteral("createLinearGradient"), method_createLinearGradient, 0);
o->defineDefaultProperty(QStringLiteral("strokeRect"), method_strokeRect, 0);
o->defineDefaultProperty(QStringLiteral("closePath"), method_closePath, 0);
+ o->defineDefaultProperty(QStringLiteral("setLineDash"), method_setLineDash, 0);
+ o->defineDefaultProperty(QStringLiteral("getLineDash"), method_getLineDash, 0);
o->defineAccessorProperty(QStringLiteral("canvas"), QQuickJSContext2DPrototype::method_get_canvas, nullptr);
return o->d();
@@ -690,6 +694,8 @@ public:
static QV4::ReturnedValue method_createImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_getImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_putImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
+ static QV4::ReturnedValue method_setLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
+ static QV4::ReturnedValue method_getLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
};
@@ -908,8 +914,8 @@ struct QQuickJSContext2DPixelData : public QV4::Object
V4_OBJECT2(QQuickJSContext2DPixelData, QV4::Object)
V4_NEEDS_DESTROY
- static QV4::ReturnedValue getIndexed(const QV4::Managed *m, uint index, bool *hasProperty);
- static bool putIndexed(QV4::Managed *m, uint index, const QV4::Value &value);
+ static QV4::ReturnedValue virtualGet(const QV4::Managed *m, QV4::PropertyKey id, const QV4::Value *receiver, bool *hasProperty);
+ static bool virtualPut(QV4::Managed *m, QV4::PropertyKey id, const QV4::Value &value, Value *receiver);
static QV4::ReturnedValue proto_get_length(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
};
@@ -954,19 +960,19 @@ static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV4::ExecutionE
{
QV4::Scope scope(v4);
QQuickContext2DEngineData *ed = engineData(scope.engine);
- QV4::Scoped<QQuickJSContext2DPixelData> pixelData(scope, scope.engine->memoryManager->allocObject<QQuickJSContext2DPixelData>());
+ QV4::Scoped<QQuickJSContext2DPixelData> pixelData(scope, scope.engine->memoryManager->allocate<QQuickJSContext2DPixelData>());
QV4::ScopedObject p(scope, ed->pixelArrayProto.value());
- pixelData->setPrototype(p);
+ pixelData->setPrototypeOf(p);
if (image.isNull()) {
*pixelData->d()->image = QImage(w, h, QImage::Format_ARGB32);
pixelData->d()->image->fill(0x00000000);
} else {
- Q_ASSERT(image.width()== qRound(w * image.devicePixelRatio()) && image.height() == qRound(h * image.devicePixelRatio()));
+ Q_ASSERT(image.width()== qRound(w * image.devicePixelRatioF()) && image.height() == qRound(h * image.devicePixelRatioF()));
*pixelData->d()->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32);
}
- QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, scope.engine->memoryManager->allocObject<QQuickJSContext2DImageData>());
+ QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, scope.engine->memoryManager->allocate<QQuickJSContext2DImageData>());
imageData->d()->pixelData = pixelData.asReturnedValue();
return imageData.asReturnedValue();
}
@@ -1582,9 +1588,9 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createLinearGradient(const
}
QQuickContext2DEngineData *ed = engineData(scope.engine);
- QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
+ QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
- gradient->setPrototype(p);
+ gradient->setPrototypeOf(p);
*gradient->d()->brush = QLinearGradient(x0, y0, x1, y1);
RETURN_RESULT(*gradient);
}
@@ -1634,9 +1640,9 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createRadialGradient(const
QQuickContext2DEngineData *ed = engineData(scope.engine);
- QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
+ QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
- gradient->setPrototype(p);
+ gradient->setPrototypeOf(p);
*gradient->d()->brush = QRadialGradient(QPointF(x1, y1), r1, QPointF(x0, y0), r0);
RETURN_RESULT(*gradient);
}
@@ -1678,9 +1684,9 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createConicalGradient(cons
QQuickContext2DEngineData *ed = engineData(scope.engine);
- QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
+ QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
- gradient->setPrototype(p);
+ gradient->setPrototypeOf(p);
*gradient->d()->brush = QConicalGradient(x, y, angle);
RETURN_RESULT(*gradient);
}
@@ -1738,7 +1744,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createPattern(const QV4::F
CHECK_CONTEXT(r)
if (argc >= 2) {
- QV4::Scoped<QQuickContext2DStyle> pattern(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
+ QV4::Scoped<QQuickContext2DStyle> pattern(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
QColor color = scope.engine->toVariant(argv[0], qMetaTypeId<QColor>()).value<QColor>();
if (color.isValid()) {
@@ -1963,6 +1969,122 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_miterLimit(const QV4::FunctionO
RETURN_UNDEFINED();
}
+/*!
+ \qmlmethod array QtQuick::Context2D::getLineDash()
+ \since QtQuick 2.11
+ Returns an array of qreals representing the dash pattern of the line.
+
+ \sa setLineDash()
+ */
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_getLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+{
+ QV4::Scope scope(b);
+ QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
+ CHECK_CONTEXT(r)
+
+ const QVector<qreal> pattern = r->d()->context->state.lineDash;
+ QV4::ScopedArrayObject array(scope, scope.engine->newArrayObject(pattern.size()));
+ array->arrayReserve(pattern.size());
+ for (int i = 0; i < pattern.size(); i++)
+ array->put(i, QV4::Primitive::fromDouble(pattern[i]));
+
+ array->setArrayLengthUnchecked(pattern.size());
+
+ RETURN_RESULT(*array);
+}
+
+/*!
+ \qmlmethod QtQuick::Context2D::setLineDash(array pattern)
+ \since QtQuick 2.11
+ Sets the dash pattern to the given pattern
+
+ \a pattern a list of numbers that specifies distances to alternately draw a line and a gap.
+
+ If the number of elements in the array is odd, the elements of the array get copied
+ and concatenated. For example, [5, 15, 25] will become [5, 15, 25, 5, 15, 25].
+
+ \table 100%
+ \row
+ \li \inlineimage qml-item-canvas-lineDash.png
+ \li
+ \code
+ var space = 4
+ ctx.setLineDash([1, space, 3, space, 9, space, 27, space, 9, space])
+ ...
+ ctx.stroke();
+ \endcode
+ \endtable
+
+ \sa setLineDash
+ */
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_setLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
+{
+ QV4::Scope scope(b);
+ QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
+ CHECK_CONTEXT_SETTER(r)
+
+ if (!argc)
+ RETURN_UNDEFINED();
+
+ QV4::ScopedArrayObject array(scope, argv[0]);
+ if (!array)
+ RETURN_UNDEFINED();
+
+ QV4::ScopedValue v(scope);
+ const uint arrayLength = array->getLength();
+ QVector<qreal> dashes;
+ dashes.reserve(arrayLength);
+ for (uint i = 0; i < arrayLength; ++i) {
+ v = array->get(i);
+ const double number = v->toNumber();
+
+ if (!qt_is_finite(number) || (number < 0))
+ RETURN_UNDEFINED();
+
+ dashes.append(v->toNumber());
+ }
+ if (dashes.size() % 2 != 0) {
+ dashes += dashes;
+ }
+
+ r->d()->context->state.lineDash = dashes;
+ r->d()->context->buffer()->setLineDash(dashes);
+
+ RETURN_UNDEFINED();
+}
+
+/*!
+ \qmlproperty real QtQuick::Context2D::lineDashOffset
+ \since QtQuick 2.11
+
+ Holds the current line dash offset
+ The default line dash ofset value is 0
+ */
+QV4::ReturnedValue QQuickJSContext2D::method_get_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+{
+ QV4::Scope scope(b);
+ QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
+ CHECK_CONTEXT(r)
+
+ RETURN_RESULT(QV4::Encode(r->d()->context->state.lineDashOffset));
+}
+
+QV4::ReturnedValue QQuickJSContext2D::method_set_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
+{
+ QV4::Scope scope(b);
+ QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
+ CHECK_CONTEXT_SETTER(r)
+
+ const qreal offset = argc ? argv[0].toNumber() : -1;
+
+ if (qt_is_finite(offset) && offset != r->d()->context->state.lineDashOffset) {
+ r->d()->context->state.lineDashOffset = offset;
+ r->d()->context->buffer()->setLineDashOffset(offset);
+ }
+ RETURN_UNDEFINED();
+}
+
+
// shadows
/*!
\qmlproperty real QtQuick::Context2D::shadowBlur
@@ -3130,8 +3252,12 @@ QV4::ReturnedValue QQuickJSContext2DPixelData::proto_get_length(const QV4::Funct
RETURN_RESULT(QV4::Encode(r->d()->image->width() * r->d()->image->height() * 4));
}
-QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(const QV4::Managed *m, uint index, bool *hasProperty)
+QV4::ReturnedValue QQuickJSContext2DPixelData::virtualGet(const QV4::Managed *m, QV4::PropertyKey id, const QV4::Value *receiver, bool *hasProperty)
{
+ if (!id.isArrayIndex())
+ return QV4::Object::virtualGet(m, id, receiver, hasProperty);
+
+ uint index = id.asArrayIndex();
Q_ASSERT(m->as<QQuickJSContext2DPixelData>());
QV4::ExecutionEngine *v4 = static_cast<const QQuickJSContext2DPixelData *>(m)->engine();
QV4::Scope scope(v4);
@@ -3156,19 +3282,24 @@ QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(const QV4::Managed *m,
return QV4::Encode(qAlpha(*pixel));
}
}
+
if (hasProperty)
*hasProperty = false;
return QV4::Encode::undefined();
}
-bool QQuickJSContext2DPixelData::putIndexed(QV4::Managed *m, uint index, const QV4::Value &value)
+bool QQuickJSContext2DPixelData::virtualPut(QV4::Managed *m, QV4::PropertyKey id, const QV4::Value &value, QV4::Value *receiver)
{
+ if (!id.isArrayIndex())
+ return Object::virtualPut(m, id, value, receiver);
+
Q_ASSERT(m->as<QQuickJSContext2DPixelData>());
QV4::ExecutionEngine *v4 = static_cast<QQuickJSContext2DPixelData *>(m)->engine();
QV4::Scope scope(v4);
if (scope.hasException())
return false;
+ uint index = id.asArrayIndex();
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, static_cast<QQuickJSContext2DPixelData *>(m));
const int v = value.toInt32();
@@ -4303,6 +4434,7 @@ QQuickContext2DEngineData::QQuickContext2DEngineData(QV4::ExecutionEngine *v4)
proto->defineAccessorProperty(QStringLiteral("lineWidth"), QQuickJSContext2D::method_get_lineWidth, QQuickJSContext2D::method_set_lineWidth);
proto->defineAccessorProperty(QStringLiteral("textAlign"), QQuickJSContext2D::method_get_textAlign, QQuickJSContext2D::method_set_textAlign);
proto->defineAccessorProperty(QStringLiteral("shadowBlur"), QQuickJSContext2D::method_get_shadowBlur, QQuickJSContext2D::method_set_shadowBlur);
+ proto->defineAccessorProperty(QStringLiteral("lineDashOffset"), QQuickJSContext2D::method_get_lineDashOffset, QQuickJSContext2D::method_set_lineDashOffset);
contextPrototype = proto;
proto = scope.engine->newObject();
@@ -4366,6 +4498,10 @@ void QQuickContext2D::popState()
if (newState.shadowOffsetY != state.shadowOffsetY)
buffer()->setShadowOffsetY(newState.shadowOffsetY);
+
+ if (newState.lineDash != state.lineDash)
+ buffer()->setLineDash(newState.lineDash);
+
m_path = state.matrix.map(m_path);
state = newState;
m_path = state.matrix.inverted().map(m_path);
@@ -4399,9 +4535,9 @@ void QQuickContext2D::setV4Engine(QV4::ExecutionEngine *engine)
QQuickContext2DEngineData *ed = engineData(engine);
QV4::Scope scope(engine);
- QV4::Scoped<QQuickJSContext2D> wrapper(scope, engine->memoryManager->allocObject<QQuickJSContext2D>());
+ QV4::Scoped<QQuickJSContext2D> wrapper(scope, engine->memoryManager->allocate<QQuickJSContext2D>());
QV4::ScopedObject p(scope, ed->contextPrototype.value());
- wrapper->setPrototype(p);
+ wrapper->setPrototypeOf(p);
wrapper->d()->context = this;
m_v4value = wrapper;
}
diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h
index 4cc07027b1..1ece6796f3 100644
--- a/src/quick/items/context2d/qquickcontext2d_p.h
+++ b/src/quick/items/context2d/qquickcontext2d_p.h
@@ -112,6 +112,8 @@ public:
LineWidth,
LineCap,
LineJoin,
+ LineDash,
+ LineDashOffset,
MiterLimit,
ShadowOffsetX,
ShadowOffsetY,
@@ -142,6 +144,7 @@ public:
, lineWidth(1)
, lineCap(Qt::FlatCap)
, lineJoin(Qt::MiterJoin)
+ , lineDashOffset(0)
, miterLimit(10)
, shadowOffsetX(0)
, shadowOffsetY(0)
@@ -170,6 +173,8 @@ public:
qreal lineWidth;
Qt::PenCapStyle lineCap;
Qt::PenJoinStyle lineJoin;
+ QVector<qreal> lineDash;
+ qreal lineDashOffset;
qreal miterLimit;
qreal shadowOffsetX;
qreal shadowOffsetY;
diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
index 30895d9b0e..55ebbe907c 100644
--- a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
+++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
@@ -192,6 +192,10 @@ QPen QQuickContext2DCommandBuffer::makePen(const QQuickContext2D::State& state)
pen.setJoinStyle(state.lineJoin);
pen.setMiterLimit(state.miterLimit);
pen.setBrush(state.strokeStyle);
+ if (!state.lineDash.isEmpty()) {
+ pen.setDashPattern(state.lineDash);
+ }
+ pen.setDashOffset(state.lineDashOffset);
return pen;
}
@@ -361,6 +365,28 @@ void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& s
p->setPen(nPen);
break;
}
+ case QQuickContext2D::LineDash:
+ {
+ const qreal count = takeReal();
+ QVector<qreal> pattern;
+ pattern.reserve(count);
+ for (uint i = 0; i < count; i++) {
+ pattern.append(takeReal());
+ }
+ state.lineDash = pattern;
+ QPen nPen = p->pen();
+ nPen.setDashPattern(pattern);
+ p->setPen(nPen);
+ break;
+ }
+ case QQuickContext2D::LineDashOffset:
+ {
+ state.lineDashOffset = takeReal();
+ QPen nPen = p->pen();
+ nPen.setDashOffset(state.lineDashOffset);
+ p->setPen(nPen);
+ break;
+ }
case QQuickContext2D::MiterLimit:
{
state.miterLimit = takeMiterLimit();
diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h
index 2a1ac7304e..c3e844c929 100644
--- a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h
+++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h
@@ -201,6 +201,20 @@ public:
ints << join;
}
+ inline void setLineDash(const QVector<qreal> &pattern)
+ {
+ commands << QQuickContext2D::LineDash;
+ reals << pattern.length();
+ for (qreal r : pattern)
+ reals << r;
+ }
+
+ inline void setLineDashOffset( qreal offset)
+ {
+ commands << QQuickContext2D::LineDashOffset;
+ reals << offset;
+ }
+
inline void setMiterLimit( qreal limit)
{
commands << QQuickContext2D::MiterLimit;
diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri
index 1acb3b5265..e649b5429d 100644
--- a/src/quick/items/items.pri
+++ b/src/quick/items/items.pri
@@ -122,9 +122,11 @@ qtConfig(quick-gridview) {
qtConfig(quick-itemview) {
HEADERS += \
+ $$PWD/qquickitemviewfxitem_p_p.h \
$$PWD/qquickitemview_p.h \
$$PWD/qquickitemview_p_p.h
SOURCES += \
+ $$PWD/qquickitemviewfxitem.cpp \
$$PWD/qquickitemview.cpp
}
@@ -142,6 +144,14 @@ qtConfig(quick-listview) {
$$PWD/qquicklistview.cpp
}
+qtConfig(quick-tableview) {
+ HEADERS += \
+ $$PWD/qquicktableview_p.h \
+ $$PWD/qquicktableview_p_p.h
+ SOURCES += \
+ $$PWD/qquicktableview.cpp
+}
+
qtConfig(quick-pathview) {
HEADERS += \
$$PWD/qquickpathview_p.h \
diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp
index 603d5a3120..18adb4e992 100644
--- a/src/quick/items/qquickanimatedsprite.cpp
+++ b/src/quick/items/qquickanimatedsprite.cpp
@@ -268,6 +268,16 @@ QT_BEGIN_NAMESPACE
Stops, then starts the sprite animation.
*/
+/*!
+ \qmlsignal QtQuick::AnimatedSprite::finished()
+ \since 5.12
+
+ This signal is emitted when the sprite has finished animating.
+
+ It is not emitted when running is set to \c false, nor for sprites whose
+ \l loops property is set to \c AnimatedSprite.Infinite.
+*/
+
QQuickAnimatedSprite::QQuickAnimatedSprite(QQuickItem *parent) :
QQuickItem(*(new QQuickAnimatedSpritePrivate), parent)
{
@@ -806,6 +816,7 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGSpriteNode *node)
frameAt = 0;
d->m_running = false;
emit runningChanged(false);
+ emit finished();
maybeUpdate();
}
} else {
diff --git a/src/quick/items/qquickanimatedsprite_p.h b/src/quick/items/qquickanimatedsprite_p.h
index d7e60b909c..ff59591c9f 100644
--- a/src/quick/items/qquickanimatedsprite_p.h
+++ b/src/quick/items/qquickanimatedsprite_p.h
@@ -135,6 +135,8 @@ Q_SIGNALS:
void loopsChanged(int arg);
void currentFrameChanged(int arg);
+ Q_REVISION(12) void finished();
+
public Q_SLOTS:
void start();
void stop();
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index d4aba31e28..31c56b7cb7 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -642,6 +642,11 @@ QQuickPointerDevice *QQuickPointerDevice::touchDevice(const QTouchDevice *d)
return dev;
}
+const QTouchDevice *QQuickPointerDevice::qTouchDevice() const
+{
+ return g_touchDevices->key(const_cast<QQuickPointerDevice *>(this));
+}
+
QList<QQuickPointerDevice*> QQuickPointerDevice::touchDevices()
{
return g_touchDevices->values();
@@ -901,7 +906,7 @@ void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, b
if (grabber) {
grabber->onGrabChanged(grabber, GrabExclusive, this);
for (QPointer<QQuickPointerHandler> passiveGrabber : m_passiveGrabbers) {
- if (passiveGrabber != grabber)
+ if (!passiveGrabber.isNull() && passiveGrabber != grabber)
passiveGrabber->onGrabChanged(grabber, OverrideGrabPassive, this);
}
}
@@ -1291,7 +1296,7 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event)
Qt::TouchPointState state = Qt::TouchPointStationary;
switch (ev->type()) {
case QEvent::MouseButtonPress:
- m_mousePoint->clearPassiveGrabbers();
+ m_point->clearPassiveGrabbers();
Q_FALLTHROUGH();
case QEvent::MouseButtonDblClick:
state = Qt::TouchPointPressed;
@@ -1305,13 +1310,13 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event)
default:
break;
}
- m_mousePoint->reset(state, ev->windowPos(), quint64(1) << 24, ev->timestamp()); // mouse has device ID 1
+ m_point->reset(state, ev->windowPos(), quint64(1) << 24, ev->timestamp()); // mouse has device ID 1
return this;
}
-void QQuickPointerMouseEvent::localize(QQuickItem *target)
+void QQuickSinglePointEvent::localize(QQuickItem *target)
{
- m_mousePoint->localizePosition(target);
+ m_point->localizePosition(target);
}
QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event)
@@ -1412,35 +1417,54 @@ QQuickPointerEvent *QQuickPointerNativeGestureEvent::reset(QEvent *event)
break;
}
quint64 deviceId = QTouchDevicePrivate::get(const_cast<QTouchDevice *>(ev->device()))->id; // a bit roundabout since QTouchDevice::mTouchDeviceId is protected
- m_gesturePoint->reset(state, ev->windowPos(), deviceId << 24, ev->timestamp());
+ m_point->reset(state, ev->windowPos(), deviceId << 24, ev->timestamp());
return this;
}
-
-void QQuickPointerNativeGestureEvent::localize(QQuickItem *target)
-{
- m_gesturePoint->localizePosition(target);
-}
#endif // QT_CONFIG(gestures)
-QQuickEventPoint *QQuickPointerMouseEvent::point(int i) const {
+QQuickEventPoint *QQuickSinglePointEvent::point(int i) const
+{
if (i == 0)
- return m_mousePoint;
+ return m_point;
return nullptr;
}
-QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const {
- if (i >= 0 && i < m_pointCount)
- return m_touchPoints.at(i);
- return nullptr;
+QQuickPointerEvent *QQuickPointerScrollEvent::reset(QEvent *event)
+{
+ m_event = static_cast<QInputEvent*>(event);
+ if (!event)
+ return this;
+#if QT_CONFIG(wheelevent)
+ if (event->type() == QEvent::Wheel) {
+ auto ev = static_cast<QWheelEvent*>(event);
+ m_device = QQuickPointerDevice::genericMouseDevice();
+ m_device->eventDeliveryTargets().clear();
+ // m_button = Qt::NoButton;
+ m_pressedButtons = ev->buttons();
+ m_angleDelta = QVector2D(ev->angleDelta());
+ m_pixelDelta = QVector2D(ev->pixelDelta());
+ m_phase = ev->phase();
+ m_synthSource = ev->source();
+ m_inverted = ev->inverted();
+
+ m_point->reset(Qt::TouchPointMoved, ev->posF(), quint64(1) << 24, ev->timestamp()); // mouse has device ID 1
+ }
+#endif
+ // TODO else if (event->type() == QEvent::Scroll) ...
+ return this;
}
-#if QT_CONFIG(gestures)
-QQuickEventPoint *QQuickPointerNativeGestureEvent::point(int i) const {
- if (i == 0)
- return m_gesturePoint;
+void QQuickPointerScrollEvent::localize(QQuickItem *target)
+{
+ m_point->localizePosition(target);
+}
+
+QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const
+{
+ if (i >= 0 && i < m_pointCount)
+ return m_touchPoints.at(i);
return nullptr;
}
-#endif // QT_CONFIG(gestures)
QQuickEventPoint::QQuickEventPoint(QQuickPointerEvent *parent)
: QObject(parent), m_pointId(0), m_exclusiveGrabber(nullptr), m_timestamp(0), m_pressTimestamp(0),
@@ -1454,17 +1478,19 @@ QQuickPointerEvent *QQuickEventPoint::pointerEvent() const
return static_cast<QQuickPointerEvent *>(parent());
}
-bool QQuickPointerMouseEvent::allPointsAccepted() const {
- return m_mousePoint->isAccepted();
+bool QQuickSinglePointEvent::allPointsAccepted() const
+{
+ return m_point->isAccepted();
}
-bool QQuickPointerMouseEvent::allUpdatedPointsAccepted() const {
- return m_mousePoint->state() == QQuickEventPoint::Pressed || m_mousePoint->isAccepted();
+bool QQuickSinglePointEvent::allUpdatedPointsAccepted() const
+{
+ return m_point->state() == QQuickEventPoint::Pressed || m_point->isAccepted();
}
-bool QQuickPointerMouseEvent::allPointsGrabbed() const
+bool QQuickSinglePointEvent::allPointsGrabbed() const
{
- return m_mousePoint->exclusiveGrabber() != nullptr;
+ return m_point->exclusiveGrabber() != nullptr;
}
QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) const
@@ -1477,10 +1503,10 @@ QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) cons
/*!
Returns the exclusive grabber of this event, if any, in a vector.
*/
-QVector<QObject *> QQuickPointerMouseEvent::exclusiveGrabbers() const
+QVector<QObject *> QQuickSinglePointEvent::exclusiveGrabbers() const
{
QVector<QObject *> result;
- if (QObject *grabber = m_mousePoint->exclusiveGrabber())
+ if (QObject *grabber = m_point->exclusiveGrabber())
result << grabber;
return result;
}
@@ -1488,17 +1514,18 @@ QVector<QObject *> QQuickPointerMouseEvent::exclusiveGrabbers() const
/*!
Remove all passive and exclusive grabbers of this event, without notifying.
*/
-void QQuickPointerMouseEvent::clearGrabbers() const {
- m_mousePoint->setGrabberItem(nullptr);
- m_mousePoint->clearPassiveGrabbers();
+void QQuickSinglePointEvent::clearGrabbers() const
+{
+ m_point->setGrabberItem(nullptr);
+ m_point->clearPassiveGrabbers();
}
/*!
Returns whether the given \a handler is the exclusive grabber of this event.
*/
-bool QQuickPointerMouseEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
+bool QQuickSinglePointEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
{
- return m_mousePoint->exclusiveGrabber() == handler;
+ return handler && (m_point->exclusiveGrabber() == handler);
}
bool QQuickPointerMouseEvent::isPressEvent() const
@@ -1526,7 +1553,8 @@ bool QQuickPointerMouseEvent::isReleaseEvent() const
return me && me->type() == QEvent::MouseButtonRelease;
}
-bool QQuickPointerTouchEvent::allPointsAccepted() const {
+bool QQuickPointerTouchEvent::allPointsAccepted() const
+{
for (int i = 0; i < m_pointCount; ++i) {
if (!m_touchPoints.at(i)->isAccepted())
return false;
@@ -1534,7 +1562,8 @@ bool QQuickPointerTouchEvent::allPointsAccepted() const {
return true;
}
-bool QQuickPointerTouchEvent::allUpdatedPointsAccepted() const {
+bool QQuickPointerTouchEvent::allUpdatedPointsAccepted() const
+{
for (int i = 0; i < m_pointCount; ++i) {
auto point = m_touchPoints.at(i);
if (point->state() != QQuickEventPoint::Pressed && !point->isAccepted())
@@ -1571,7 +1600,8 @@ QVector<QObject *> QQuickPointerTouchEvent::exclusiveGrabbers() const
Remove all passive and exclusive grabbers of all touchpoints in this event,
without notifying.
*/
-void QQuickPointerTouchEvent::clearGrabbers() const {
+void QQuickPointerTouchEvent::clearGrabbers() const
+{
for (auto point: m_touchPoints) {
point->setGrabberItem(nullptr);
point->clearPassiveGrabbers();
@@ -1629,7 +1659,8 @@ QVector<QPointF> QQuickPointerEvent::unacceptedPressedPointScenePositions() cons
If the touchpoint cannot be found, this returns nullptr.
Ownership of the event is NOT transferred to the caller.
*/
-QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const {
+QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const
+{
const QTouchEvent::TouchPoint *p = touchPointById(pointID);
if (!p)
return nullptr;
@@ -1669,33 +1700,6 @@ QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickIte
}
#if QT_CONFIG(gestures)
-/*!
- Returns the exclusive grabber of this event, if any, in a vector.
-*/
-QVector<QObject *> QQuickPointerNativeGestureEvent::exclusiveGrabbers() const
-{
- QVector<QObject *> result;
- if (QObject *grabber = m_gesturePoint->exclusiveGrabber())
- result << grabber;
- return result;
-}
-
-/*!
- Remove all passive and exclusive grabbers of this event, without notifying.
-*/
-void QQuickPointerNativeGestureEvent::clearGrabbers() const {
- m_gesturePoint->setGrabberItem(nullptr);
- m_gesturePoint->clearPassiveGrabbers();
-}
-
-/*!
- Returns whether the given \a handler is the exclusive grabber of this event.
-*/
-bool QQuickPointerNativeGestureEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
-{
- return m_gesturePoint->exclusiveGrabber() == handler;
-}
-
bool QQuickPointerNativeGestureEvent::isPressEvent() const
{
return type() == Qt::BeginNativeGesture;
@@ -1729,6 +1733,39 @@ qreal QQuickPointerNativeGestureEvent::value() const
#endif // QT_CONFIG(gestures)
/*!
+ Returns whether the scroll event has Qt::ScrollBegin phase. On touchpads
+ which provide phase information, this is true when the fingers are placed
+ on the touchpad and scrolling begins. On other devices where this
+ information is not available, it remains false.
+*/
+bool QQuickPointerScrollEvent::isPressEvent() const
+{
+ return phase() == Qt::ScrollBegin;
+}
+
+/*!
+ Returns true when the scroll event has Qt::ScrollUpdate phase, or when the
+ phase is unknown. Some multi-touch-capable touchpads and trackpads provide
+ phase information; whereas ordinary mouse wheels and other types of
+ trackpads do not, and in such cases this is always true.
+*/
+bool QQuickPointerScrollEvent::isUpdateEvent() const
+{
+ return phase() == Qt::ScrollUpdate || phase() == Qt::NoScrollPhase;
+}
+
+/*!
+ Returns whether the scroll event has Qt::ScrollBegin phase. On touchpads
+ which provide phase information, this is true when the fingers are lifted
+ from the touchpad. On other devices where this information is not
+ available, it remains false.
+*/
+bool QQuickPointerScrollEvent::isReleaseEvent() const
+{
+ return phase() == Qt::ScrollEnd;
+}
+
+/*!
\internal
Returns a pointer to the QQuickEventPoint which has the \a pointId as
\l {QQuickEventPoint::pointId}{pointId}.
@@ -1736,13 +1773,15 @@ qreal QQuickPointerNativeGestureEvent::value() const
\fn QQuickPointerEvent::pointById(int pointId) const
*/
-QQuickEventPoint *QQuickPointerMouseEvent::pointById(int pointId) const {
- if (m_mousePoint && pointId == m_mousePoint->pointId())
- return m_mousePoint;
+QQuickEventPoint *QQuickSinglePointEvent::pointById(int pointId) const
+{
+ if (m_point && pointId == m_point->pointId())
+ return m_point;
return nullptr;
}
-QQuickEventPoint *QQuickPointerTouchEvent::pointById(int pointId) const {
+QQuickEventPoint *QQuickPointerTouchEvent::pointById(int pointId) const
+{
auto it = std::find_if(m_touchPoints.constBegin(), m_touchPoints.constEnd(),
[pointId](const QQuickEventTouchPoint *tp) { return tp->pointId() == pointId; } );
if (it != m_touchPoints.constEnd())
@@ -1750,21 +1789,14 @@ QQuickEventPoint *QQuickPointerTouchEvent::pointById(int pointId) const {
return nullptr;
}
-#if QT_CONFIG(gestures)
-QQuickEventPoint *QQuickPointerNativeGestureEvent::pointById(int pointId) const {
- if (m_gesturePoint && pointId == m_gesturePoint->pointId())
- return m_gesturePoint;
- return nullptr;
-}
-#endif
-
/*!
\internal
Returns a pointer to the original TouchPoint which has the same
\l {QTouchEvent::TouchPoint::id}{id} as \a pointId, if the original event is a
QTouchEvent, and if that point is found. Otherwise, returns nullptr.
*/
-const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int pointId) const {
+const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int pointId) const
+{
const QTouchEvent *ev = asTouchEvent();
if (!ev)
return nullptr;
@@ -1875,24 +1907,10 @@ QTouchEvent *QQuickPointerTouchEvent::asTouchEvent() const
return static_cast<QTouchEvent *>(m_event);
}
-#if QT_CONFIG(gestures)
-bool QQuickPointerNativeGestureEvent::allPointsAccepted() const {
- return m_gesturePoint->isAccepted();
-}
-
-bool QQuickPointerNativeGestureEvent::allUpdatedPointsAccepted() const {
- return m_gesturePoint->state() == QQuickEventPoint::Pressed || m_gesturePoint->isAccepted();
-}
-
-bool QQuickPointerNativeGestureEvent::allPointsGrabbed() const
-{
- return m_gesturePoint->exclusiveGrabber() != nullptr;
-}
-#endif // QT_CONFIG(gestures)
-
#ifndef QT_NO_DEBUG_STREAM
-Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *dev) {
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *dev)
+{
QDebugStateSaver saver(dbg);
dbg.nospace();
if (!dev) {
@@ -1914,7 +1932,8 @@ Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *
return dbg;
}
-Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *event) {
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *event)
+{
QDebugStateSaver saver(dbg);
dbg.nospace();
dbg << "QQuickPointerEvent(";
@@ -1933,7 +1952,8 @@ Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *e
return dbg;
}
-Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *event) {
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *event)
+{
QDebugStateSaver saver(dbg);
dbg.nospace();
dbg << "QQuickEventPoint(accepted:" << event->isAccepted()
diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h
index bb6726706d..c67c386676 100644
--- a/src/quick/items/qquickevents_p_p.h
+++ b/src/quick/items/qquickevents_p_p.h
@@ -69,6 +69,7 @@ class QQuickPointerMouseEvent;
#if QT_CONFIG(gestures)
class QQuickPointerNativeGestureEvent;
#endif
+class QQuickPointerScrollEvent;
class QQuickPointerTabletEvent;
class QQuickPointerTouchEvent;
class QQuickPointerHandler;
@@ -419,17 +420,19 @@ public: // helpers for C++ only (during event delivery)
#if QT_CONFIG(gestures)
virtual QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() { return nullptr; }
#endif
+ virtual QQuickPointerScrollEvent *asPointerScrollEvent() { return nullptr; }
virtual const QQuickPointerMouseEvent *asPointerMouseEvent() const { return nullptr; }
virtual const QQuickPointerTouchEvent *asPointerTouchEvent() const { return nullptr; }
virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; }
#if QT_CONFIG(gestures)
virtual const QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() const { return nullptr; }
#endif
+ virtual const QQuickPointerScrollEvent *asPointerScrollEvent() const { return nullptr; }
virtual bool allPointsAccepted() const = 0;
virtual bool allUpdatedPointsAccepted() const = 0;
virtual bool allPointsGrabbed() const = 0;
- bool isAccepted() { return m_event->isAccepted(); }
- void setAccepted(bool accepted) { m_event->setAccepted(accepted); }
+ bool isAccepted() { return m_event ? m_event->isAccepted() : false; }
+ void setAccepted(bool accepted) { if (m_event) m_event->setAccepted(accepted); }
QVector<QPointF> unacceptedPressedPointScenePositions() const;
virtual int pointCount() const = 0;
@@ -439,7 +442,7 @@ public: // helpers for C++ only (during event delivery)
virtual void clearGrabbers() const = 0;
virtual bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const = 0;
- ulong timestamp() const { return m_event->timestamp(); }
+ ulong timestamp() const { return m_event ? m_event->timestamp() : 0; }
protected:
QQuickPointerDevice *m_device;
@@ -450,21 +453,14 @@ protected:
Q_DISABLE_COPY(QQuickPointerEvent)
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPointerMouseEvent : public QQuickPointerEvent
+class Q_QUICK_PRIVATE_EXPORT QQuickSinglePointEvent : public QQuickPointerEvent
{
Q_OBJECT
public:
- QQuickPointerMouseEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
- : QQuickPointerEvent(parent, device), m_mousePoint(new QQuickEventPoint(this)) { }
+ QQuickSinglePointEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
+ : QQuickPointerEvent(parent, device), m_point(new QQuickEventPoint(this)) { }
- QQuickPointerEvent *reset(QEvent *) override;
void localize(QQuickItem *target) override;
- bool isPressEvent() const override;
- bool isDoubleClickEvent() const override;
- bool isUpdateEvent() const override;
- bool isReleaseEvent() const override;
- QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; }
- const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; }
int pointCount() const override { return 1; }
QQuickEventPoint *point(int i) const override;
QQuickEventPoint *pointById(int pointId) const override;
@@ -475,10 +471,28 @@ public:
void clearGrabbers() const override;
bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const override;
- QMouseEvent *asMouseEvent(const QPointF& localPos) const;
+protected:
+ QQuickEventPoint *m_point;
-private:
- QQuickEventPoint *m_mousePoint;
+ Q_DISABLE_COPY(QQuickSinglePointEvent)
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerMouseEvent : public QQuickSinglePointEvent
+{
+ Q_OBJECT
+public:
+ QQuickPointerMouseEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
+ : QQuickSinglePointEvent(parent, device) { }
+
+ QQuickPointerEvent *reset(QEvent *) override;
+ bool isPressEvent() const override;
+ bool isDoubleClickEvent() const override;
+ bool isUpdateEvent() const override;
+ bool isReleaseEvent() const override;
+ QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; }
+ const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; }
+
+ QMouseEvent *asMouseEvent(const QPointF& localPos) const;
Q_DISABLE_COPY(QQuickPointerMouseEvent)
};
@@ -526,7 +540,7 @@ private:
};
#if QT_CONFIG(gestures)
-class Q_QUICK_PRIVATE_EXPORT QQuickPointerNativeGestureEvent : public QQuickPointerEvent
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerNativeGestureEvent : public QQuickSinglePointEvent
{
Q_OBJECT
Q_PROPERTY(Qt::NativeGestureType type READ type CONSTANT)
@@ -534,34 +548,65 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerNativeGestureEvent : public QQuickPoin
public:
QQuickPointerNativeGestureEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
- : QQuickPointerEvent(parent, device), m_gesturePoint(new QQuickEventPoint(this)) { }
+ : QQuickSinglePointEvent(parent, device) { }
QQuickPointerEvent *reset(QEvent *) override;
- void localize(QQuickItem *target) override;
bool isPressEvent() const override;
bool isUpdateEvent() const override;
bool isReleaseEvent() const override;
QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() override { return this; }
const QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() const override { return this; }
- int pointCount() const override { return 1; }
- QQuickEventPoint *point(int i) const override;
- QQuickEventPoint *pointById(int pointId) const override;
- bool allPointsAccepted() const override;
- bool allUpdatedPointsAccepted() const override;
- bool allPointsGrabbed() const override;
- QVector<QObject *> exclusiveGrabbers() const override;
- void clearGrabbers() const override;
- bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const override;
Qt::NativeGestureType type() const;
qreal value() const;
-private:
- QQuickEventPoint *m_gesturePoint;
-
Q_DISABLE_COPY(QQuickPointerNativeGestureEvent)
};
#endif // QT_CONFIG(gestures)
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerScrollEvent : public QQuickSinglePointEvent
+{
+ Q_OBJECT
+ Q_PROPERTY(QVector2D angleDelta READ angleDelta CONSTANT)
+ Q_PROPERTY(QVector2D pixelDelta READ pixelDelta CONSTANT)
+ Q_PROPERTY(bool hasAngleDelta READ hasAngleDelta CONSTANT)
+ Q_PROPERTY(bool hasPixelDelta READ hasPixelDelta CONSTANT)
+ Q_PROPERTY(bool inverted READ isInverted CONSTANT)
+
+public:
+ QQuickPointerScrollEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
+ : QQuickSinglePointEvent(parent, device) { }
+
+ QQuickPointerEvent *reset(QEvent *) override;
+ void localize(QQuickItem *target) override;
+ bool isPressEvent() const override;
+ bool isUpdateEvent() const override;
+ bool isReleaseEvent() const override;
+ QQuickPointerScrollEvent *asPointerScrollEvent() override { return this; }
+ const QQuickPointerScrollEvent *asPointerScrollEvent() const override { return this; }
+ QVector2D angleDelta() const { return m_angleDelta; }
+ QVector2D pixelDelta() const { return m_pixelDelta; }
+ bool hasAngleDelta() const { return !angleDelta().isNull(); }
+ bool hasPixelDelta() const { return !pixelDelta().isNull(); }
+ bool isInverted() const { return m_inverted; }
+ Qt::ScrollPhase phase() const { return m_phase; }
+
+private:
+ // TODO add QQuickPointerDevice source() whenever QInputEvent is extended to have a source device
+ // then maybe Qt::MouseEventSource synthSource() will be obsolete... that's why it's not public now
+ Qt::MouseEventSource synthSource() const { return m_synthSource; }
+
+private:
+ QVector2D m_angleDelta;
+ QVector2D m_pixelDelta;
+ Qt::ScrollPhase m_phase = Qt::NoScrollPhase;
+ Qt::MouseEventSource m_synthSource = Qt::MouseEventNotSynthesized;
+ bool m_inverted = false;
+
+ friend class QQuickWindowPrivate;
+
+ Q_DISABLE_COPY(QQuickPointerScrollEvent)
+};
+
// ### Qt 6: move this to qtbase, replace QTouchDevice and the enums in QTabletEvent
class Q_QUICK_PRIVATE_EXPORT QQuickPointerDevice : public QObject
@@ -627,6 +672,7 @@ public:
int buttonCount() const { return m_buttonCount; }
QString name() const { return m_name; }
QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; }
+ const QTouchDevice *qTouchDevice() const;
static QQuickPointerDevice *touchDevice(const QTouchDevice *d);
static QList<QQuickPointerDevice *> touchDevices();
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index cc35630fff..b47d055ca4 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -248,6 +248,7 @@ QQuickFlickablePrivate::QQuickFlickablePrivate()
, stealMouse(false), pressed(false)
, scrollingPhase(false), interactive(true), calcVelocity(false)
, pixelAligned(false)
+ , syncDrag(false)
, lastPosTime(-1)
, lastPressTime(0)
, deceleration(QML_FLICK_DEFAULTDECELERATION)
@@ -984,6 +985,33 @@ void QQuickFlickable::setPixelAligned(bool align)
}
}
+/*!
+ \qmlproperty bool QtQuick::Flickable::synchronousDrag
+ \since 5.12
+
+ If this property is set to true, then when the mouse or touchpoint moves
+ far enough to begin dragging the content, the content will jump, such that
+ the content pixel which was under the cursor or touchpoint when pressed
+ remains under that point.
+
+ The default is \c false, which provides a smoother experience (no jump)
+ at the cost that some of the drag distance is "lost" at the beginning.
+*/
+bool QQuickFlickable::synchronousDrag() const
+{
+ Q_D(const QQuickFlickable);
+ return d->syncDrag;
+}
+
+void QQuickFlickable::setSynchronousDrag(bool v)
+{
+ Q_D(QQuickFlickable);
+ if (v != d->syncDrag) {
+ d->syncDrag = v;
+ emit synchronousDragChanged();
+ }
+}
+
qint64 QQuickFlickablePrivate::computeCurrentTime(QInputEvent *event) const
{
if (0 != event->timestamp())
@@ -1100,7 +1128,7 @@ void QQuickFlickablePrivate::drag(qint64 currentTimestamp, QEvent::Type eventTyp
if (overThreshold || elapsedSincePress > 200) {
if (!vMoved)
vData.dragStartOffset = dy;
- qreal newY = dy + vData.pressPos - vData.dragStartOffset;
+ qreal newY = dy + vData.pressPos - (syncDrag ? 0 : vData.dragStartOffset);
// Recalculate bounds in case margins have changed, but use the content
// size estimate taken at the start of the drag in case the drag causes
// the estimate to be altered
@@ -1176,7 +1204,7 @@ void QQuickFlickablePrivate::drag(qint64 currentTimestamp, QEvent::Type eventTyp
if (overThreshold || elapsedSincePress > 200) {
if (!hMoved)
hData.dragStartOffset = dx;
- qreal newX = dx + hData.pressPos - hData.dragStartOffset;
+ qreal newX = dx + hData.pressPos - (syncDrag ? 0 : hData.dragStartOffset);
const qreal minX = hData.dragMinBound + hData.startMargin;
const qreal maxX = hData.dragMaxBound - hData.endMargin;
if (!(boundsBehavior & QQuickFlickable::DragOverBounds)) {
diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h
index 939e3af698..45921c8b86 100644
--- a/src/quick/items/qquickflickable_p.h
+++ b/src/quick/items/qquickflickable_p.h
@@ -106,6 +106,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickFlickable : public QQuickItem
Q_PROPERTY(QQuickFlickableVisibleArea *visibleArea READ visibleArea CONSTANT)
Q_PROPERTY(bool pixelAligned READ pixelAligned WRITE setPixelAligned NOTIFY pixelAlignedChanged)
+ Q_PROPERTY(bool synchronousDrag READ synchronousDrag WRITE setSynchronousDrag NOTIFY synchronousDragChanged REVISION 12)
Q_PROPERTY(qreal horizontalOvershoot READ horizontalOvershoot NOTIFY horizontalOvershootChanged REVISION 9)
Q_PROPERTY(qreal verticalOvershoot READ verticalOvershoot NOTIFY verticalOvershootChanged REVISION 9)
@@ -213,6 +214,9 @@ public:
bool pixelAligned() const;
void setPixelAligned(bool align);
+ bool synchronousDrag() const;
+ void setSynchronousDrag(bool v);
+
qreal horizontalOvershoot() const;
qreal verticalOvershoot() const;
@@ -259,6 +263,7 @@ Q_SIGNALS:
void dragStarted();
void dragEnded();
void pixelAlignedChanged();
+ Q_REVISION(12) void synchronousDragChanged();
Q_REVISION(9) void horizontalOvershootChanged();
Q_REVISION(9) void verticalOvershootChanged();
diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h
index 08f069e830..dfb943d710 100644
--- a/src/quick/items/qquickflickable_p_p.h
+++ b/src/quick/items/qquickflickable_p_p.h
@@ -221,6 +221,7 @@ public:
bool interactive : 1;
bool calcVelocity : 1;
bool pixelAligned : 1;
+ bool syncDrag : 1;
QElapsedTimer timer;
QBasicTimer movementEndingTimer;
qint64 lastPosTime;
diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp
index f2fa66332c..6fcd846423 100644
--- a/src/quick/items/qquickgridview.cpp
+++ b/src/quick/items/qquickgridview.cpp
@@ -914,7 +914,7 @@ void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
if (snapMode == QQuickGridView::SnapOneRow && moveReason == Mouse) {
// if we've been dragged < rowSize()/2 then bias towards the next row
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = 0;
if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < rowSize()/2)
bias = rowSize()/2;
@@ -925,13 +925,13 @@ void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
tempPosition -= bias;
}
FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
- if (strictHighlightRange && currentItem && (!topItem || topItem->index != currentIndex)) {
+ if (strictHighlightRange && currentItem && (!topItem || (topItem->index != currentIndex && fixupMode == Immediate))) {
// StrictlyEnforceRange always keeps an item in range
updateHighlight();
topItem = currentItem;
}
FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
- if (strictHighlightRange && currentItem && (!bottomItem || bottomItem->index != currentIndex)) {
+ if (strictHighlightRange && currentItem && (!bottomItem || (bottomItem->index != currentIndex && fixupMode == Immediate))) {
// StrictlyEnforceRange always keeps an item in range
updateHighlight();
bottomItem = currentItem;
@@ -1013,7 +1013,7 @@ bool QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
if (data.move.value() < minExtent) {
if (snapMode == QQuickGridView::SnapOneRow) {
// if we've been dragged < averageSize/2 then bias towards the next item
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = dist < rowSize()/2 ? rowSize()/2 : 0;
if (isContentFlowReversed())
bias = -bias;
@@ -1030,7 +1030,7 @@ bool QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
if (data.move.value() > maxExtent) {
if (snapMode == QQuickGridView::SnapOneRow) {
// if we've been dragged < averageSize/2 then bias towards the next item
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = -dist < rowSize()/2 ? rowSize()/2 : 0;
if (isContentFlowReversed())
bias = -bias;
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 29b0ed10e6..c1e625ef95 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -67,6 +67,7 @@
#include <QtQuick/private/qquickstate_p.h>
#include <private/qquickitem_p.h>
#include <QtQuick/private/qquickaccessibleattached_p.h>
+#include <QtQuick/private/qquickhoverhandler_p.h>
#include <QtQuick/private/qquickpointerhandler_p.h>
#include <private/qv4engine_p.h>
@@ -5150,6 +5151,17 @@ void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event)
}
}
+bool QQuickItemPrivate::anyPointerHandlerWants(QQuickEventPoint *point) const
+{
+ if (!hasPointerHandlers())
+ return false;
+ for (QQuickPointerHandler *handler : extra->pointerHandlers) {
+ if (handler->wantsEventPoint(point))
+ return true;
+ }
+ return false;
+}
+
/*!
\internal
Deliver the \a event to all PointerHandlers which are in the pre-determined
@@ -7353,6 +7365,8 @@ void QQuickItemPrivate::setHasHoverInChild(bool hasHover)
QQuickItemPrivate *otherChildPrivate = QQuickItemPrivate::get(otherChild);
if (otherChildPrivate->subtreeHoverEnabled || otherChildPrivate->hoverEnabled)
return; // nope! sorry, something else wants it kept on.
+ if (otherChildPrivate->hasHoverHandlers())
+ return; // nope! sorry, we have pointer handlers which are interested.
}
}
@@ -8126,6 +8140,16 @@ bool QQuickItemPrivate::hasPointerHandlers() const
return extra.isAllocated() && !extra->pointerHandlers.isEmpty();
}
+bool QQuickItemPrivate::hasHoverHandlers() const
+{
+ if (!hasPointerHandlers())
+ return false;
+ for (QQuickPointerHandler *h : extra->pointerHandlers)
+ if (qmlobject_cast<QQuickHoverHandler *>(h))
+ return true;
+ return false;
+}
+
#if QT_CONFIG(quick_shadereffect)
QQuickItemLayer::QQuickItemLayer(QQuickItem *item)
: m_item(item)
@@ -8711,7 +8735,7 @@ void QV4::Heap::QQuickItemWrapper::markObjects(QV4::Heap::Base *that, QV4::MarkS
quint64 QQuickItemPrivate::_q_createJSWrapper(QV4::ExecutionEngine *engine)
{
- return (engine->memoryManager->allocObject<QQuickItemWrapper>(q_func()))->asReturnedValue();
+ return (engine->memoryManager->allocate<QQuickItemWrapper>(q_func()))->asReturnedValue();
}
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index 93287cf0aa..bb63bc1ee4 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -280,6 +280,7 @@ public:
QQuickItemLayer *layer() const;
bool hasPointerHandlers() const;
+ bool hasHoverHandlers() const;
// data property
static void data_append(QQmlListProperty<QObject> *, QObject *);
@@ -581,6 +582,7 @@ public:
#endif
void deliverShortcutOverrideEvent(QKeyEvent *);
+ bool anyPointerHandlerWants(QQuickEventPoint *point) const;
virtual bool handlePointerEvent(QQuickPointerEvent *, bool avoidExclusiveGrabber = false);
virtual void setVisible(bool visible);
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index 51a91e1f7a..806fd9e465 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -422,6 +422,10 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickAnimatedImage, 11>(uri, 2, 11,"AnimatedImage");
#endif
qmlRegisterType<QQuickItem, 11>(uri, 2, 11,"Item");
+
+ qmlRegisterType<QQuickAnimatedSprite, 12>("QtQuick", 2, 12, "AnimatedSprite");
+ qmlRegisterType<QQuickGradient, 12>(uri, 2, 12, "Gradient");
+ qmlRegisterType<QQuickFlickable, 12>(uri, 2, 12, "Flickable");
}
static void initResources()
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index f2e055e874..4e4881ce19 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qquickitemview_p_p.h"
+#include "qquickitemviewfxitem_p_p.h"
#include <QtQuick/private/qquicktransition_p.h>
#include <QtQml/QQmlInfo>
#include "qplatformdefs.h"
@@ -52,117 +53,14 @@ Q_LOGGING_CATEGORY(lcItemViewDelegateLifecycle, "qt.quick.itemview.lifecycle")
#endif
FxViewItem::FxViewItem(QQuickItem *i, QQuickItemView *v, bool own, QQuickItemViewAttached *attached)
- : item(i)
+ : QQuickItemViewFxItem(i, own, QQuickItemViewPrivate::get(v))
, view(v)
- , transitionableItem(nullptr)
, attached(attached)
- , ownItem(own)
- , releaseAfterTransition(false)
- , trackGeom(false)
{
if (attached) // can be null for default components (see createComponentItem)
attached->setView(view);
}
-FxViewItem::~FxViewItem()
-{
- delete transitionableItem;
- if (ownItem && item) {
- trackGeometry(false);
- item->setParentItem(nullptr);
- item->deleteLater();
- item = nullptr;
- }
-}
-
-qreal FxViewItem::itemX() const
-{
- return transitionableItem ? transitionableItem->itemX() : (item ? item->x() : 0);
-}
-
-qreal FxViewItem::itemY() const
-{
- return transitionableItem ? transitionableItem->itemY() : (item ? item->y() : 0);
-}
-
-void FxViewItem::moveTo(const QPointF &pos, bool immediate)
-{
- if (transitionableItem)
- transitionableItem->moveTo(pos, immediate);
- else if (item)
- item->setPosition(pos);
-}
-
-void FxViewItem::setVisible(bool visible)
-{
- if (!visible && transitionableItem && transitionableItem->transitionScheduledOrRunning())
- return;
- if (item)
- QQuickItemPrivate::get(item)->setCulled(!visible);
-}
-
-void FxViewItem::trackGeometry(bool track)
-{
- if (track) {
- if (!trackGeom) {
- if (item) {
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
- itemPrivate->addItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
- }
- trackGeom = true;
- }
- } else {
- if (trackGeom) {
- if (item) {
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
- itemPrivate->removeItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
- }
- trackGeom = false;
- }
- }
-}
-
-QQuickItemViewTransitioner::TransitionType FxViewItem::scheduledTransitionType() const
-{
- return transitionableItem ? transitionableItem->nextTransitionType : QQuickItemViewTransitioner::NoTransition;
-}
-
-bool FxViewItem::transitionScheduledOrRunning() const
-{
- return transitionableItem ? transitionableItem->transitionScheduledOrRunning() : false;
-}
-
-bool FxViewItem::transitionRunning() const
-{
- return transitionableItem ? transitionableItem->transitionRunning() : false;
-}
-
-bool FxViewItem::isPendingRemoval() const
-{
- return transitionableItem ? transitionableItem->isPendingRemoval() : false;
-}
-
-void FxViewItem::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget)
-{
- if (!transitioner)
- return;
- if (!transitionableItem)
- transitionableItem = new QQuickItemViewTransitionableItem(item);
- transitioner->transitionNextReposition(transitionableItem, type, asTarget);
-}
-
-bool FxViewItem::prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds)
-{
- return transitionableItem ? transitionableItem->prepareTransition(transitioner, index, viewBounds) : false;
-}
-
-void FxViewItem::startTransition(QQuickItemViewTransitioner *transitioner)
-{
- if (transitionableItem)
- transitionableItem->startTransition(transitioner, index);
-}
-
-
QQuickItemViewChangeSet::QQuickItemViewChangeSet()
: active(false)
{
diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h
index e250cf0ccb..ea5b5df9c6 100644
--- a/src/quick/items/qquickitemview_p_p.h
+++ b/src/quick/items/qquickitemview_p_p.h
@@ -56,6 +56,7 @@
QT_REQUIRE_CONFIG(quick_itemview);
#include "qquickitemview_p.h"
+#include "qquickitemviewfxitem_p_p.h"
#include "qquickitemviewtransition_p.h"
#include "qquickflickable_p_p.h"
#include <QtQml/private/qqmlobjectmodel_p.h>
@@ -65,47 +66,13 @@ QT_REQUIRE_CONFIG(quick_itemview);
QT_BEGIN_NAMESPACE
-
-class Q_AUTOTEST_EXPORT FxViewItem
+class Q_AUTOTEST_EXPORT FxViewItem : public QQuickItemViewFxItem
{
public:
FxViewItem(QQuickItem *, QQuickItemView *, bool own, QQuickItemViewAttached *attached);
- virtual ~FxViewItem();
-
- qreal itemX() const;
- qreal itemY() const;
- inline qreal itemWidth() const { return item ? item->width() : 0; }
- inline qreal itemHeight() const { return item ? item->height() : 0; }
-
- void moveTo(const QPointF &pos, bool immediate);
- void setVisible(bool visible);
- void trackGeometry(bool track);
-
- QQuickItemViewTransitioner::TransitionType scheduledTransitionType() const;
- bool transitionScheduledOrRunning() const;
- bool transitionRunning() const;
- bool isPendingRemoval() const;
-
- void transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget);
- bool prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds);
- void startTransition(QQuickItemViewTransitioner *transitioner);
-
- // these are positions and sizes along the current direction of scrolling/flicking
- virtual qreal position() const = 0;
- virtual qreal endPosition() const = 0;
- virtual qreal size() const = 0;
- virtual qreal sectionSize() const = 0;
-
- virtual bool contains(qreal x, qreal y) const = 0;
- QPointer<QQuickItem> item;
QQuickItemView *view;
- QQuickItemViewTransitionableItem *transitionableItem;
QQuickItemViewAttached *attached;
- int index;
- bool ownItem;
- bool releaseAfterTransition;
- bool trackGeom;
};
diff --git a/src/quick/items/qquickitemviewfxitem.cpp b/src/quick/items/qquickitemviewfxitem.cpp
new file mode 100644
index 0000000000..f9c65967ea
--- /dev/null
+++ b/src/quick/items/qquickitemviewfxitem.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickitemviewfxitem_p_p.h"
+#include "qquickitem_p.h"
+#include "qquickitemview_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QQuickItemViewFxItem::QQuickItemViewFxItem(QQuickItem *item, bool ownItem, QQuickItemChangeListener* changeListener)
+ : item(item)
+ , ownItem(ownItem)
+ , changeListener(changeListener)
+ , transitionableItem(nullptr)
+ , releaseAfterTransition(false)
+ , trackGeom(false)
+{
+}
+
+QQuickItemViewFxItem::~QQuickItemViewFxItem()
+{
+ delete transitionableItem;
+ if (ownItem && item) {
+ trackGeometry(false);
+ item->setParentItem(0);
+ item->deleteLater();
+ }
+}
+
+qreal QQuickItemViewFxItem::itemX() const
+{
+ return transitionableItem ? transitionableItem->itemX() : (item ? item->x() : 0);
+}
+
+qreal QQuickItemViewFxItem::itemY() const
+{
+ return transitionableItem ? transitionableItem->itemY() : (item ? item->y() : 0);
+}
+
+void QQuickItemViewFxItem::moveTo(const QPointF &pos, bool immediate)
+{
+ if (transitionableItem)
+ transitionableItem->moveTo(pos, immediate);
+ else if (item)
+ item->setPosition(pos);
+}
+
+void QQuickItemViewFxItem::setVisible(bool visible)
+{
+ if (!visible && transitionableItem && transitionableItem->transitionScheduledOrRunning())
+ return;
+ if (item)
+ QQuickItemPrivate::get(item)->setCulled(!visible);
+}
+
+void QQuickItemViewFxItem::trackGeometry(bool track)
+{
+ if (track) {
+ if (!trackGeom) {
+ if (item) {
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ itemPrivate->addItemChangeListener(changeListener, QQuickItemPrivate::Geometry);
+ }
+ trackGeom = true;
+ }
+ } else {
+ if (trackGeom) {
+ if (item) {
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ itemPrivate->removeItemChangeListener(changeListener, QQuickItemPrivate::Geometry);
+ }
+ trackGeom = false;
+ }
+ }
+}
+
+QRectF QQuickItemViewFxItem::geometry() const
+{
+ return QRectF(item->position(), item->size());
+}
+
+void QQuickItemViewFxItem::setGeometry(const QRectF &geometry)
+{
+ item->setPosition(geometry.topLeft());
+ item->setSize(geometry.size());
+}
+
+QQuickItemViewTransitioner::TransitionType QQuickItemViewFxItem::scheduledTransitionType() const
+{
+ return transitionableItem ? transitionableItem->nextTransitionType : QQuickItemViewTransitioner::NoTransition;
+}
+
+bool QQuickItemViewFxItem::transitionScheduledOrRunning() const
+{
+ return transitionableItem ? transitionableItem->transitionScheduledOrRunning() : false;
+}
+
+bool QQuickItemViewFxItem::transitionRunning() const
+{
+ return transitionableItem ? transitionableItem->transitionRunning() : false;
+}
+
+bool QQuickItemViewFxItem::isPendingRemoval() const
+{
+ return transitionableItem ? transitionableItem->isPendingRemoval() : false;
+}
+
+void QQuickItemViewFxItem::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget)
+{
+ if (!transitioner)
+ return;
+ if (!transitionableItem)
+ transitionableItem = new QQuickItemViewTransitionableItem(item);
+ transitioner->transitionNextReposition(transitionableItem, type, asTarget);
+}
+
+bool QQuickItemViewFxItem::prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds)
+{
+ return transitionableItem ? transitionableItem->prepareTransition(transitioner, index, viewBounds) : false;
+}
+
+void QQuickItemViewFxItem::startTransition(QQuickItemViewTransitioner *transitioner)
+{
+ if (transitionableItem)
+ transitionableItem->startTransition(transitioner, index);
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/quick/items/qquickitemviewfxitem_p_p.h b/src/quick/items/qquickitemviewfxitem_p_p.h
new file mode 100644
index 0000000000..48ffe248bc
--- /dev/null
+++ b/src/quick/items/qquickitemviewfxitem_p_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKFXVIEWITEM_P_P_H
+#define QQUICKFXVIEWITEM_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/private/qtquickglobal_p.h>
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickitemviewtransition_p.h>
+
+QT_REQUIRE_CONFIG(quick_itemview);
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickItemViewFxItem
+{
+public:
+ QQuickItemViewFxItem(QQuickItem *item, bool ownItem, QQuickItemChangeListener *changeListener);
+ virtual ~QQuickItemViewFxItem();
+
+ qreal itemX() const;
+ qreal itemY() const;
+ inline qreal itemWidth() const { return item ? item->width() : 0; }
+ inline qreal itemHeight() const { return item ? item->height() : 0; }
+
+ void moveTo(const QPointF &pos, bool immediate);
+ void setVisible(bool visible);
+ void trackGeometry(bool track);
+
+ QRectF geometry() const;
+ void setGeometry(const QRectF &geometry);
+
+ QQuickItemViewTransitioner::TransitionType scheduledTransitionType() const;
+ bool transitionScheduledOrRunning() const;
+ bool transitionRunning() const;
+ bool isPendingRemoval() const;
+
+ void transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget);
+ bool prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds);
+ void startTransition(QQuickItemViewTransitioner *transitioner);
+
+ // these are positions and sizes along the current direction of scrolling/flicking
+ virtual qreal position() const = 0;
+ virtual qreal endPosition() const = 0;
+ virtual qreal size() const = 0;
+ virtual qreal sectionSize() const = 0;
+
+ virtual bool contains(qreal x, qreal y) const = 0;
+
+ int index = -1;
+ QPointer<QQuickItem> item;
+ bool ownItem;
+ QQuickItemChangeListener *changeListener;
+ QQuickItemViewTransitionableItem *transitionableItem;
+ bool releaseAfterTransition;
+ bool trackGeom;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKFXVIEWITEM_P_P_H
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 19033e03f1..7c364692c2 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -1501,7 +1501,7 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
if (snapMode == QQuickListView::SnapOneItem && moveReason == Mouse) {
// if we've been dragged < averageSize/2 then bias towards the next item
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = 0;
if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2)
bias = averageSize/2;
@@ -1512,13 +1512,13 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
tempPosition -= bias;
}
FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
- if (strictHighlightRange && currentItem && (!topItem || topItem->index != currentIndex)) {
+ if (strictHighlightRange && currentItem && (!topItem || (topItem->index != currentIndex && fixupMode == Immediate))) {
// StrictlyEnforceRange always keeps an item in range
updateHighlight();
topItem = currentItem;
}
FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
- if (strictHighlightRange && currentItem && (!bottomItem || bottomItem->index != currentIndex)) {
+ if (strictHighlightRange && currentItem && (!bottomItem || (bottomItem->index != currentIndex && fixupMode == Immediate))) {
// StrictlyEnforceRange always keeps an item in range
updateHighlight();
bottomItem = currentItem;
@@ -1599,7 +1599,7 @@ bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
if (data.move.value() < minExtent) {
if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
// if we've been dragged < averageSize/2 then bias towards the next item
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
if (isContentFlowReversed())
bias = -bias;
@@ -1616,7 +1616,7 @@ bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
if (data.move.value() > maxExtent) {
if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
// if we've been dragged < averageSize/2 then bias towards the next item
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
if (isContentFlowReversed())
bias = -bias;
diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp
index b5ae41daef..f32f25a42f 100644
--- a/src/quick/items/qquickloader.cpp
+++ b/src/quick/items/qquickloader.cpp
@@ -54,7 +54,7 @@ static const QQuickItemPrivate::ChangeTypes watchedChanges
= QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
QQuickLoaderPrivate::QQuickLoaderPrivate()
- : item(nullptr), object(nullptr), component(nullptr), itemContext(nullptr), incubator(nullptr), updatingSize(false),
+ : item(nullptr), object(nullptr), itemContext(nullptr), incubator(nullptr), updatingSize(false),
active(true), loadingFromSource(false), asynchronous(false)
{
}
@@ -111,11 +111,10 @@ void QQuickLoaderPrivate::clear()
QObject::disconnect(component, SIGNAL(progressChanged(qreal)),
q, SIGNAL(progressChanged()));
component->deleteLater();
- component = nullptr;
+ component.setObject(nullptr, q);
} else if (component) {
- component = nullptr;
+ component.setObject(nullptr, q);
}
- componentStrongReference.clear();
source = QUrl();
if (item) {
@@ -441,7 +440,7 @@ void QQuickLoader::loadFromSource()
if (isComponentComplete()) {
QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
if (!d->component)
- d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
+ d->component.setObject(new QQmlComponent(qmlEngine(this), d->source, mode, this), this);
d->load();
}
}
@@ -484,11 +483,7 @@ void QQuickLoader::setSourceComponent(QQmlComponent *comp)
d->clear();
- d->component = comp;
- if (comp) {
- if (QQmlData *ddata = QQmlData::get(comp))
- d->componentStrongReference = ddata->jsWrapper;
- }
+ d->component.setObject(comp, this);
d->loadingFromSource = false;
if (d->active)
@@ -832,7 +827,7 @@ void QQuickLoader::componentComplete()
if (d->loadingFromSource) {
QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
if (!d->component)
- d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
+ d->component.setObject(new QQmlComponent(qmlEngine(this), d->source, mode, this), this);
}
d->load();
}
diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h
index 7492527401..349b5c6c06 100644
--- a/src/quick/items/qquickloader_p_p.h
+++ b/src/quick/items/qquickloader_p_p.h
@@ -103,8 +103,7 @@ public:
QUrl source;
QQuickItem *item;
QObject *object;
- QQmlComponent *component;
- QV4::PersistentValue componentStrongReference; // To ensure GC doesn't delete components created by Qt.createComponent
+ QQmlStrongJSQObjectReference<QQmlComponent> component;
QQmlContext *itemContext;
QQuickLoaderIncubator *incubator;
QV4::PersistentValue initialPropertyValues;
diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp
index bf030b9d80..5e217dcd0c 100644
--- a/src/quick/items/qquickrectangle.cpp
+++ b/src/quick/items/qquickrectangle.cpp
@@ -40,9 +40,13 @@
#include "qquickrectangle_p.h"
#include "qquickrectangle_p_p.h"
+#include <QtQml/qqmlinfo.h>
+
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qsgadaptationlayer_p.h>
+#include <private/qqmlmetatype_p.h>
+
#include <QtGui/qpixmapcache.h>
#include <QtCore/qmath.h>
#include <QtCore/qmetaobject.h>
@@ -219,10 +223,11 @@ void QQuickGradientStop::updateGradient()
of solid color fills or images. Consider using gradients for static items
in a user interface.
- In Qt 5.0, only vertical, linear gradients can be applied to items. If you
- need to apply different orientations of gradients, a combination of rotation
- and clipping will need to be applied to the relevant items. This can
- introduce additional performance requirements for your application.
+ Since Qt 5.12, vertical and horizontal linear gradients can be applied to items.
+ If you need to apply angled gradients, a combination of rotation and clipping
+ can be applied to the relevant items. Alternatively, consider using
+ QtQuick.Shapes::LinearGradient or QtGraphicalEffects::LinearGradient. These
+ approaches can all introduce additional performance requirements for your application.
The use of animations involving gradient stops may not give the desired
result. An alternative way to animate gradients is to use pre-generated
@@ -255,6 +260,28 @@ QQmlListProperty<QQuickGradientStop> QQuickGradient::stops()
return QQmlListProperty<QQuickGradientStop>(this, m_stops);
}
+/*!
+ \qmlproperty enumeration QtQuick::Gradient::orientation
+ \since 5.12
+
+ Set this property to define the direction of the gradient.
+ \list
+ \li Gradient.Vertical - a vertical gradient
+ \li Gradient.Horizontal - a horizontal gradient
+ \endlist
+
+ The default is Gradient.Vertical.
+*/
+void QQuickGradient::setOrientation(Orientation orientation)
+{
+ if (m_orientation == orientation)
+ return;
+
+ m_orientation = orientation;
+ emit orientationChanged();
+ emit updated();
+}
+
QGradientStops QQuickGradient::gradientStops() const
{
QGradientStops stops;
@@ -370,11 +397,11 @@ QQuickPen *QQuickRectangle::border()
}
/*!
- \qmlproperty Gradient QtQuick::Rectangle::gradient
+ \qmlproperty any QtQuick::Rectangle::gradient
The gradient to use to fill the rectangle.
- This property allows for the construction of simple vertical gradients.
+ This property allows for the construction of simple vertical or horizontal gradients.
Other gradients may be formed by adding rotation to the rectangle.
\div {class="float-left"}
@@ -384,37 +411,66 @@ QQuickPen *QQuickRectangle::border()
\snippet qml/rectangle/rectangle-gradient.qml rectangles
\clearfloat
+ The property also accepts gradient presets from QGradient::Preset. Note however
+ that due to Rectangle only supporting simple vertical or horizontal gradients,
+ any preset with an unsupported angle will revert to the closest representation.
+
+ \snippet qml/rectangle/rectangle-gradient.qml presets
+ \clearfloat
+
If both a gradient and a color are specified, the gradient will be used.
\sa Gradient, color
*/
-QQuickGradient *QQuickRectangle::gradient() const
+QJSValue QQuickRectangle::gradient() const
{
Q_D(const QQuickRectangle);
return d->gradient;
}
-void QQuickRectangle::setGradient(QQuickGradient *gradient)
+void QQuickRectangle::setGradient(const QJSValue &gradient)
{
Q_D(QQuickRectangle);
- if (d->gradient == gradient)
+ if (d->gradient.equals(gradient))
return;
- static int updatedSignalIdx = -1;
- if (updatedSignalIdx < 0)
- updatedSignalIdx = QMetaMethod::fromSignal(&QQuickGradient::updated).methodIndex();
+
+ static int updatedSignalIdx = QMetaMethod::fromSignal(&QQuickGradient::updated).methodIndex();
if (d->doUpdateSlotIdx < 0)
d->doUpdateSlotIdx = QQuickRectangle::staticMetaObject.indexOfSlot("doUpdate()");
- if (d->gradient)
- QMetaObject::disconnect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
- d->gradient = gradient;
- if (d->gradient)
- QMetaObject::connect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
+
+ if (auto oldGradient = qobject_cast<QQuickGradient*>(d->gradient.toQObject()))
+ QMetaObject::disconnect(oldGradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
+
+ if (gradient.isQObject()) {
+ if (auto newGradient = qobject_cast<QQuickGradient*>(gradient.toQObject())) {
+ d->gradient = gradient;
+ QMetaObject::connect(newGradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
+ } else {
+ qmlWarning(this) << "Can't assign "
+ << QQmlMetaType::prettyTypeName(gradient.toQObject()) << " to gradient property";
+ d->gradient = QJSValue();
+ }
+ } else if (gradient.isNumber() || gradient.isString()) {
+ QGradient preset(gradient.toVariant().value<QGradient::Preset>());
+ if (preset.type() != QGradient::NoGradient) {
+ d->gradient = gradient;
+ } else {
+ qmlWarning(this) << "No such gradient preset '" << gradient.toString() << "'";
+ d->gradient = QJSValue();
+ }
+ } else if (gradient.isNull() || gradient.isUndefined()) {
+ d->gradient = gradient;
+ } else {
+ qmlWarning(this) << "Unknown gradient type. Expected int, string, or Gradient";
+ d->gradient = QJSValue();
+ }
+
update();
}
void QQuickRectangle::resetGradient()
{
- setGradient(nullptr);
+ setGradient(QJSValue());
}
/*!
@@ -510,10 +566,35 @@ QSGNode *QQuickRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
rectangle->setAntialiasing(antialiasing());
QGradientStops stops;
- if (d->gradient) {
- stops = d->gradient->gradientStops();
+ bool vertical = true;
+ if (d->gradient.isQObject()) {
+ auto gradient = qobject_cast<QQuickGradient*>(d->gradient.toQObject());
+ Q_ASSERT(gradient);
+ stops = gradient->gradientStops();
+ vertical = gradient->orientation() == QQuickGradient::Vertical;
+ } else if (d->gradient.isNumber() || d->gradient.isString()) {
+ QGradient preset(d->gradient.toVariant().value<QGradient::Preset>());
+ if (preset.type() == QGradient::LinearGradient) {
+ auto linearGradient = static_cast<QLinearGradient&>(preset);
+ const QPointF start = linearGradient.start();
+ const QPointF end = linearGradient.finalStop();
+ vertical = qAbs(start.y() - end.y()) >= qAbs(start.x() - end.x());
+ stops = linearGradient.stops();
+ if ((vertical && start.y() > end.y()) || (!vertical && start.x() > end.x())) {
+ // QSGInternalRectangleNode doesn't support stops in the wrong order,
+ // so we need to manually reverse them here.
+ QGradientStops reverseStops;
+ for (auto it = stops.rbegin(); it != stops.rend(); ++it) {
+ auto stop = *it;
+ stop.first = 1 - stop.first;
+ reverseStops.append(stop);
+ }
+ stops = reverseStops;
+ }
+ }
}
rectangle->setGradientStops(stops);
+ rectangle->setGradientVertical(vertical);
rectangle->update();
diff --git a/src/quick/items/qquickrectangle_p.h b/src/quick/items/qquickrectangle_p.h
index c07ad835fb..d56a03d22d 100644
--- a/src/quick/items/qquickrectangle_p.h
+++ b/src/quick/items/qquickrectangle_p.h
@@ -119,24 +119,35 @@ class Q_QUICK_PRIVATE_EXPORT QQuickGradient : public QObject
Q_OBJECT
Q_PROPERTY(QQmlListProperty<QQuickGradientStop> stops READ stops)
+ Q_PROPERTY(Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged REVISION 12)
Q_CLASSINFO("DefaultProperty", "stops")
+ Q_ENUMS(QGradient::Preset)
public:
QQuickGradient(QObject *parent=nullptr);
~QQuickGradient() override;
+ enum Orientation { Vertical = Qt::Vertical,
+ Horizontal = Qt::Horizontal };
+ Q_ENUM(Orientation)
+
QQmlListProperty<QQuickGradientStop> stops();
+ Orientation orientation() const { return m_orientation; }
+ void setOrientation(Orientation orientation);
+
QGradientStops gradientStops() const;
Q_SIGNALS:
void updated();
+ void orientationChanged();
private:
void doUpdate();
private:
QList<QQuickGradientStop *> m_stops;
+ Orientation m_orientation = Vertical;
friend class QQuickRectangle;
friend class QQuickGradientStop;
};
@@ -147,7 +158,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickRectangle : public QQuickItem
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
- Q_PROPERTY(QQuickGradient *gradient READ gradient WRITE setGradient RESET resetGradient)
+ Q_PROPERTY(QJSValue gradient READ gradient WRITE setGradient RESET resetGradient)
Q_PROPERTY(QQuickPen * border READ border CONSTANT)
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
public:
@@ -158,8 +169,8 @@ public:
QQuickPen *border();
- QQuickGradient *gradient() const;
- void setGradient(QQuickGradient *gradient);
+ QJSValue gradient() const;
+ void setGradient(const QJSValue &gradient);
void resetGradient();
qreal radius() const;
diff --git a/src/quick/items/qquickrectangle_p_p.h b/src/quick/items/qquickrectangle_p_p.h
index 3c1aaf7661..c7c5293f9b 100644
--- a/src/quick/items/qquickrectangle_p_p.h
+++ b/src/quick/items/qquickrectangle_p_p.h
@@ -73,7 +73,7 @@ public:
}
QColor color;
- QQuickGradient *gradient;
+ QJSValue gradient;
QQuickPen *pen;
qreal radius;
static int doUpdateSlotIdx;
diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp
index fb1c6366f2..86a64cdeeb 100644
--- a/src/quick/items/qquickrendercontrol.cpp
+++ b/src/quick/items/qquickrendercontrol.cpp
@@ -187,7 +187,6 @@ void QQuickRenderControlPrivate::windowDestroyed()
{
if (window) {
rc->invalidate();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
delete QQuickWindowPrivate::get(window)->animationController;
QQuickWindowPrivate::get(window)->animationController = nullptr;
diff --git a/src/quick/items/qquickscalegrid.cpp b/src/quick/items/qquickscalegrid.cpp
index d7a0f1b681..23f179be1d 100644
--- a/src/quick/items/qquickscalegrid.cpp
+++ b/src/quick/items/qquickscalegrid.cpp
@@ -66,6 +66,7 @@ void QQuickScaleGrid::setLeft(int pos)
{
if (_left != pos) {
_left = pos;
+ emit leftBorderChanged();
emit borderChanged();
}
}
@@ -74,6 +75,7 @@ void QQuickScaleGrid::setTop(int pos)
{
if (_top != pos) {
_top = pos;
+ emit topBorderChanged();
emit borderChanged();
}
}
@@ -82,6 +84,7 @@ void QQuickScaleGrid::setRight(int pos)
{
if (_right != pos) {
_right = pos;
+ emit rightBorderChanged();
emit borderChanged();
}
}
@@ -90,6 +93,7 @@ void QQuickScaleGrid::setBottom(int pos)
{
if (_bottom != pos) {
_bottom = pos;
+ emit bottomBorderChanged();
emit borderChanged();
}
}
diff --git a/src/quick/items/qquickscalegrid_p_p.h b/src/quick/items/qquickscalegrid_p_p.h
index 5752f61e3f..f5187a8eea 100644
--- a/src/quick/items/qquickscalegrid_p_p.h
+++ b/src/quick/items/qquickscalegrid_p_p.h
@@ -65,10 +65,10 @@ class Q_AUTOTEST_EXPORT QQuickScaleGrid : public QObject
{
Q_OBJECT
- Q_PROPERTY(int left READ left WRITE setLeft NOTIFY borderChanged)
- Q_PROPERTY(int top READ top WRITE setTop NOTIFY borderChanged)
- Q_PROPERTY(int right READ right WRITE setRight NOTIFY borderChanged)
- Q_PROPERTY(int bottom READ bottom WRITE setBottom NOTIFY borderChanged)
+ Q_PROPERTY(int left READ left WRITE setLeft NOTIFY leftBorderChanged)
+ Q_PROPERTY(int top READ top WRITE setTop NOTIFY topBorderChanged)
+ Q_PROPERTY(int right READ right WRITE setRight NOTIFY rightBorderChanged)
+ Q_PROPERTY(int bottom READ bottom WRITE setBottom NOTIFY bottomBorderChanged)
public:
QQuickScaleGrid(QObject *parent=nullptr);
@@ -90,6 +90,10 @@ public:
Q_SIGNALS:
void borderChanged();
+ void leftBorderChanged();
+ void topBorderChanged();
+ void rightBorderChanged();
+ void bottomBorderChanged();
private:
int _left;
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
new file mode 100644
index 0000000000..26aa409cec
--- /dev/null
+++ b/src/quick/items/qquicktableview.cpp
@@ -0,0 +1,1463 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquicktableview_p.h"
+#include "qquicktableview_p_p.h"
+
+#include <QtCore/qtimer.h>
+#include <QtCore/qdir.h>
+#include <QtQml/private/qqmldelegatemodel_p.h>
+#include <QtQml/private/qqmldelegatemodel_p_p.h>
+#include <QtQml/private/qqmlincubator_p.h>
+#include <QtQml/private/qqmlchangeset_p.h>
+#include <QtQml/qqmlinfo.h>
+
+#include <QtQuick/private/qquickflickable_p_p.h>
+#include <QtQuick/private/qquickitemviewfxitem_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcTableViewDelegateLifecycle, "qt.quick.tableview.lifecycle")
+
+#define Q_TABLEVIEW_UNREACHABLE(output) { dumpTable(); qWarning() << "output:" << output; Q_UNREACHABLE(); }
+#define Q_TABLEVIEW_ASSERT(cond, output) Q_ASSERT(cond || [&](){ dumpTable(); qWarning() << "output:" << output; return false;}())
+
+static const Qt::Edge allTableEdges[] = { Qt::LeftEdge, Qt::RightEdge, Qt::TopEdge, Qt::BottomEdge };
+static const int kBufferTimerInterval = 300;
+
+static QLine rectangleEdge(const QRect &rect, Qt::Edge tableEdge)
+{
+ switch (tableEdge) {
+ case Qt::LeftEdge:
+ return QLine(rect.topLeft(), rect.bottomLeft());
+ case Qt::RightEdge:
+ return QLine(rect.topRight(), rect.bottomRight());
+ case Qt::TopEdge:
+ return QLine(rect.topLeft(), rect.topRight());
+ case Qt::BottomEdge:
+ return QLine(rect.bottomLeft(), rect.bottomRight());
+ }
+ return QLine();
+}
+
+static QRect expandedRect(const QRect &rect, Qt::Edge edge, int increment)
+{
+ switch (edge) {
+ case Qt::LeftEdge:
+ return rect.adjusted(-increment, 0, 0, 0);
+ case Qt::RightEdge:
+ return rect.adjusted(0, 0, increment, 0);
+ case Qt::TopEdge:
+ return rect.adjusted(0, -increment, 0, 0);
+ case Qt::BottomEdge:
+ return rect.adjusted(0, 0, 0, increment);
+ }
+ return QRect();
+}
+
+const QPoint QQuickTableViewPrivate::kLeft = QPoint(-1, 0);
+const QPoint QQuickTableViewPrivate::kRight = QPoint(1, 0);
+const QPoint QQuickTableViewPrivate::kUp = QPoint(0, -1);
+const QPoint QQuickTableViewPrivate::kDown = QPoint(0, 1);
+
+QQuickTableViewPrivate::QQuickTableViewPrivate()
+ : QQuickFlickablePrivate()
+{
+ cacheBufferDelayTimer.setSingleShot(true);
+ QObject::connect(&cacheBufferDelayTimer, &QTimer::timeout, [=]{ loadBuffer(); });
+}
+
+QQuickTableViewPrivate::~QQuickTableViewPrivate()
+{
+ clear();
+}
+
+QString QQuickTableViewPrivate::tableLayoutToString() const
+{
+ return QString(QLatin1String("table cells: (%1,%2) -> (%3,%4), item count: %5, table rect: %6,%7 x %8,%9"))
+ .arg(loadedTable.topLeft().x()).arg(loadedTable.topLeft().y())
+ .arg(loadedTable.bottomRight().x()).arg(loadedTable.bottomRight().y())
+ .arg(loadedItems.count())
+ .arg(loadedTableOuterRect.x())
+ .arg(loadedTableOuterRect.y())
+ .arg(loadedTableOuterRect.width())
+ .arg(loadedTableOuterRect.height());
+}
+
+void QQuickTableViewPrivate::dumpTable() const
+{
+ auto listCopy = loadedItems.values();
+ std::stable_sort(listCopy.begin(), listCopy.end(),
+ [](const FxTableItem *lhs, const FxTableItem *rhs)
+ { return lhs->index < rhs->index; });
+
+ qWarning() << QStringLiteral("******* TABLE DUMP *******");
+ for (int i = 0; i < listCopy.count(); ++i)
+ qWarning() << static_cast<FxTableItem *>(listCopy.at(i))->cell;
+ qWarning() << tableLayoutToString();
+
+ const QString filename = QStringLiteral("QQuickTableView_dumptable_capture.png");
+ const QString path = QDir::current().absoluteFilePath(filename);
+ if (q_func()->window()->grabWindow().save(path))
+ qWarning() << "Window capture saved to:" << path;
+}
+
+QQuickTableViewAttached *QQuickTableViewPrivate::getAttachedObject(const QObject *object) const
+{
+ QObject *attachedObject = qmlAttachedPropertiesObject<QQuickTableView>(object);
+ return static_cast<QQuickTableViewAttached *>(attachedObject);
+}
+
+int QQuickTableViewPrivate::modelIndexAtCell(const QPoint &cell) const
+{
+ int availableRows = tableSize.height();
+ int modelIndex = cell.y() + (cell.x() * availableRows);
+ Q_TABLEVIEW_ASSERT(modelIndex < model->count(),
+ "modelIndex:" << modelIndex << "cell:" << cell << "count:" << model->count());
+ return modelIndex;
+}
+
+QPoint QQuickTableViewPrivate::cellAtModelIndex(int modelIndex) const
+{
+ int availableRows = tableSize.height();
+ Q_TABLEVIEW_ASSERT(availableRows > 0, availableRows);
+ int column = int(modelIndex / availableRows);
+ int row = modelIndex % availableRows;
+ return QPoint(column, row);
+}
+
+void QQuickTableViewPrivate::updateContentWidth()
+{
+ Q_Q(QQuickTableView);
+
+ const qreal thresholdBeforeAdjust = 0.1;
+ int currentRightColumn = loadedTable.right();
+
+ if (currentRightColumn > contentSizeBenchMarkPoint.x()) {
+ contentSizeBenchMarkPoint.setX(currentRightColumn);
+
+ qreal currentWidth = loadedTableOuterRect.right();
+ qreal averageCellSize = currentWidth / (currentRightColumn + 1);
+ qreal averageSize = averageCellSize + cellSpacing.width();
+ qreal estimatedWith = (tableSize.width() * averageSize) - cellSpacing.width();
+
+ // loadedTableOuterRect has already been adjusted for left margin
+ currentWidth += tableMargins.right();
+ estimatedWith += tableMargins.right();
+
+ if (currentRightColumn >= tableSize.width() - 1) {
+ // We are at the last column, and can set the exact width
+ if (currentWidth != q->implicitWidth())
+ q->setContentWidth(currentWidth);
+ } else if (currentWidth >= q->implicitWidth()) {
+ // We are at the estimated width, but there are still more columns
+ q->setContentWidth(estimatedWith);
+ } else {
+ // Only set a new width if the new estimate is substantially different
+ qreal diff = 1 - (estimatedWith / q->implicitWidth());
+ if (qAbs(diff) > thresholdBeforeAdjust)
+ q->setContentWidth(estimatedWith);
+ }
+ }
+}
+
+void QQuickTableViewPrivate::updateContentHeight()
+{
+ Q_Q(QQuickTableView);
+
+ const qreal thresholdBeforeAdjust = 0.1;
+ int currentBottomRow = loadedTable.bottom();
+
+ if (currentBottomRow > contentSizeBenchMarkPoint.y()) {
+ contentSizeBenchMarkPoint.setY(currentBottomRow);
+
+ qreal currentHeight = loadedTableOuterRect.bottom();
+ qreal averageCellSize = currentHeight / (currentBottomRow + 1);
+ qreal averageSize = averageCellSize + cellSpacing.height();
+ qreal estimatedHeight = (tableSize.height() * averageSize) - cellSpacing.height();
+
+ // loadedTableOuterRect has already been adjusted for top margin
+ currentHeight += tableMargins.bottom();
+ estimatedHeight += tableMargins.bottom();
+
+ if (currentBottomRow >= tableSize.height() - 1) {
+ // We are at the last row, and can set the exact height
+ if (currentHeight != q->implicitHeight())
+ q->setContentHeight(currentHeight);
+ } else if (currentHeight >= q->implicitHeight()) {
+ // We are at the estimated height, but there are still more rows
+ q->setContentHeight(estimatedHeight);
+ } else {
+ // Only set a new height if the new estimate is substantially different
+ qreal diff = 1 - (estimatedHeight / q->implicitHeight());
+ if (qAbs(diff) > thresholdBeforeAdjust)
+ q->setContentHeight(estimatedHeight);
+ }
+ }
+}
+
+void QQuickTableViewPrivate::enforceFirstRowColumnAtOrigo()
+{
+ // Gaps before the first row/column can happen if rows/columns
+ // changes size while flicking e.g because of spacing changes or
+ // changes to a column maxWidth/row maxHeight. Check for this, and
+ // move the whole table rect accordingly.
+ bool layoutNeeded = false;
+ const qreal flickMargin = 50;
+
+ if (loadedTable.x() == 0 && loadedTableOuterRect.x() != tableMargins.left()) {
+ // The table is at the beginning, but not at the edge of the
+ // content view. So move the table to origo.
+ loadedTableOuterRect.moveLeft(tableMargins.left());
+ layoutNeeded = true;
+ } else if (loadedTableOuterRect.x() < 0) {
+ // The table is outside the beginning of the content view. Move
+ // the whole table inside, and make some room for flicking.
+ loadedTableOuterRect.moveLeft(tableMargins.left() + loadedTable.x() == 0 ? 0 : flickMargin);
+ layoutNeeded = true;
+ }
+
+ if (loadedTable.y() == 0 && loadedTableOuterRect.y() != tableMargins.top()) {
+ loadedTableOuterRect.moveTop(tableMargins.top());
+ layoutNeeded = true;
+ } else if (loadedTableOuterRect.y() < 0) {
+ loadedTableOuterRect.moveTop(tableMargins.top() + loadedTable.y() == 0 ? 0 : flickMargin);
+ layoutNeeded = true;
+ }
+
+ if (layoutNeeded)
+ relayoutTableItems();
+}
+
+void QQuickTableViewPrivate::syncLoadedTableRectFromLoadedTable()
+{
+ QRectF topLeftRect = loadedTableItem(loadedTable.topLeft())->geometry();
+ QRectF bottomRightRect = loadedTableItem(loadedTable.bottomRight())->geometry();
+ loadedTableOuterRect = topLeftRect.united(bottomRightRect);
+ loadedTableInnerRect = QRectF(topLeftRect.bottomRight(), bottomRightRect.topLeft());
+}
+
+void QQuickTableViewPrivate::syncLoadedTableFromLoadRequest()
+{
+ switch (loadRequest.edge()) {
+ case Qt::LeftEdge:
+ case Qt::TopEdge:
+ loadedTable.setTopLeft(loadRequest.firstCell());
+ break;
+ case Qt::RightEdge:
+ case Qt::BottomEdge:
+ loadedTable.setBottomRight(loadRequest.lastCell());
+ break;
+ default:
+ loadedTable = QRect(loadRequest.firstCell(), loadRequest.lastCell());
+ }
+}
+
+FxTableItem *QQuickTableViewPrivate::itemNextTo(const FxTableItem *fxTableItem, const QPoint &direction) const
+{
+ return loadedTableItem(fxTableItem->cell + direction);
+}
+
+FxTableItem *QQuickTableViewPrivate::loadedTableItem(const QPoint &cell) const
+{
+ const int modelIndex = modelIndexAtCell(cell);
+ Q_TABLEVIEW_ASSERT(loadedItems.contains(modelIndex), modelIndex << cell);
+ return loadedItems.value(modelIndex);
+}
+
+FxTableItem *QQuickTableViewPrivate::createFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode)
+{
+ Q_Q(QQuickTableView);
+
+ bool ownItem = false;
+ int modelIndex = modelIndexAtCell(cell);
+
+ QObject* object = model->object(modelIndex, incubationMode);
+ if (!object) {
+ if (model->incubationStatus(modelIndex) == QQmlIncubator::Loading) {
+ // Item is incubating. Return nullptr for now, and let the table call this
+ // function again once we get a callback to itemCreatedCallback().
+ return nullptr;
+ }
+
+ qWarning() << "TableView: failed loading index:" << modelIndex;
+ object = new QQuickItem();
+ ownItem = true;
+ }
+
+ QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
+ if (!item) {
+ // The model could not provide an QQuickItem for the
+ // given index, so we create a placeholder instead.
+ qWarning() << "TableView: delegate is not an item:" << modelIndex;
+ model->release(object);
+ item = new QQuickItem();
+ ownItem = true;
+ }
+
+ item->setParentItem(q->contentItem());
+
+ FxTableItem *fxTableItem = new FxTableItem(item, q, ownItem);
+ fxTableItem->setVisible(false);
+ fxTableItem->cell = cell;
+ fxTableItem->index = modelIndex;
+ return fxTableItem;
+}
+
+FxTableItem *QQuickTableViewPrivate::loadFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode)
+{
+#ifdef QT_DEBUG
+ // Since TableView needs to work flawlessly when e.g incubating inside an async
+ // loader, being able to override all loading to async while debugging can be helpful.
+ static const bool forcedAsync = forcedIncubationMode == QLatin1String("async");
+ if (forcedAsync)
+ incubationMode = QQmlIncubator::Asynchronous;
+#endif
+
+ // Note that even if incubation mode is asynchronous, the item might
+ // be ready immediately since the model has a cache of items.
+ QBoolBlocker guard(blockItemCreatedCallback);
+ auto item = createFxTableItem(cell, incubationMode);
+ qCDebug(lcTableViewDelegateLifecycle) << cell << "ready?" << bool(item);
+ return item;
+}
+
+void QQuickTableViewPrivate::releaseLoadedItems() {
+ // Make a copy and clear the list of items first to avoid destroyed
+ // items being accessed during the loop (QTBUG-61294)
+ auto const tmpList = loadedItems;
+ loadedItems.clear();
+ for (FxTableItem *item : tmpList)
+ releaseItem(item);
+}
+
+void QQuickTableViewPrivate::releaseItem(FxTableItem *fxTableItem)
+{
+ if (fxTableItem->item) {
+ if (fxTableItem->ownItem)
+ delete fxTableItem->item;
+ else if (model->release(fxTableItem->item) != QQmlInstanceModel::Destroyed)
+ fxTableItem->item->setParentItem(nullptr);
+ }
+
+ delete fxTableItem;
+}
+
+void QQuickTableViewPrivate::clear()
+{
+ tableInvalid = true;
+ tableRebuilding = false;
+ if (loadRequest.isActive())
+ cancelLoadRequest();
+
+ releaseLoadedItems();
+ loadedTable = QRect();
+ loadedTableOuterRect = QRect();
+ loadedTableInnerRect = QRect();
+ columnWidths.clear();
+ rowHeights.clear();
+ contentSizeBenchMarkPoint = QPoint(-1, -1);
+
+ updateContentWidth();
+ updateContentHeight();
+}
+
+void QQuickTableViewPrivate::unloadItem(const QPoint &cell)
+{
+ const int modelIndex = modelIndexAtCell(cell);
+ Q_TABLEVIEW_ASSERT(loadedItems.contains(modelIndex), modelIndex << cell);
+ releaseItem(loadedItems.take(modelIndex));
+}
+
+void QQuickTableViewPrivate::unloadItems(const QLine &items)
+{
+ qCDebug(lcTableViewDelegateLifecycle) << items;
+
+ if (items.dx()) {
+ int y = items.p1().y();
+ for (int x = items.p1().x(); x <= items.p2().x(); ++x)
+ unloadItem(QPoint(x, y));
+ } else {
+ int x = items.p1().x();
+ for (int y = items.p1().y(); y <= items.p2().y(); ++y)
+ unloadItem(QPoint(x, y));
+ }
+}
+
+bool QQuickTableViewPrivate::canLoadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const
+{
+ switch (tableEdge) {
+ case Qt::LeftEdge:
+ if (loadedTable.topLeft().x() == 0)
+ return false;
+ return loadedTableOuterRect.left() > fillRect.left() + cellSpacing.width();
+ case Qt::RightEdge:
+ if (loadedTable.bottomRight().x() >= tableSize.width() - 1)
+ return false;
+ return loadedTableOuterRect.right() < fillRect.right() - cellSpacing.width();
+ case Qt::TopEdge:
+ if (loadedTable.topLeft().y() == 0)
+ return false;
+ return loadedTableOuterRect.top() > fillRect.top() + cellSpacing.height();
+ case Qt::BottomEdge:
+ if (loadedTable.bottomRight().y() >= tableSize.height() - 1)
+ return false;
+ return loadedTableOuterRect.bottom() < fillRect.bottom() - cellSpacing.height();
+ }
+
+ return false;
+}
+
+bool QQuickTableViewPrivate::canUnloadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const
+{
+ // Note: if there is only one row or column left, we cannot unload, since
+ // they are needed as anchor point for further layouting.
+ switch (tableEdge) {
+ case Qt::LeftEdge:
+ if (loadedTable.width() <= 1)
+ return false;
+ return loadedTableInnerRect.left() < fillRect.left();
+ case Qt::RightEdge:
+ if (loadedTable.width() <= 1)
+ return false;
+ return loadedTableInnerRect.right() > fillRect.right();
+ case Qt::TopEdge:
+ if (loadedTable.height() <= 1)
+ return false;
+ return loadedTableInnerRect.top() < fillRect.top();
+ case Qt::BottomEdge:
+ if (loadedTable.height() <= 1)
+ return false;
+ return loadedTableInnerRect.bottom() > fillRect.bottom();
+ }
+ Q_TABLEVIEW_UNREACHABLE(tableEdge);
+ return false;
+}
+
+Qt::Edge QQuickTableViewPrivate::nextEdgeToLoad(const QRectF rect)
+{
+ for (Qt::Edge edge : allTableEdges) {
+ if (canLoadTableEdge(edge, rect))
+ return edge;
+ }
+ return Qt::Edge(0);
+}
+
+Qt::Edge QQuickTableViewPrivate::nextEdgeToUnload(const QRectF rect)
+{
+ for (Qt::Edge edge : allTableEdges) {
+ if (canUnloadTableEdge(edge, rect))
+ return edge;
+ }
+ return Qt::Edge(0);
+}
+
+qreal QQuickTableViewPrivate::cellWidth(const QPoint& cell)
+{
+ // If a delegate item has TableView.cellWidth set, then
+ // we prefer that. Otherwise we fall back to use implicitWidth.
+ // Using an items width directly is not an option, since we change
+ // it during layout (which would also cause problems when recycling items).
+ auto const cellItem = loadedTableItem(cell)->item;
+ if (auto const attached = getAttachedObject(cellItem)) {
+ if (!attached->m_cellWidth.isNull)
+ return attached->m_cellWidth;
+ }
+ return cellItem->implicitWidth();
+}
+
+qreal QQuickTableViewPrivate::cellHeight(const QPoint& cell)
+{
+ // If a delegate item has TableView.cellHeight set, then
+ // we prefer that. Otherwise we fall back to use implicitHeight.
+ // Using an items height directly is not an option, since we change
+ // it during layout (which would also cause problems when recycling items).
+ auto const cellItem = loadedTableItem(cell)->item;
+ if (auto const attached = getAttachedObject(cellItem)) {
+ if (!attached->m_cellHeight.isNull)
+ return attached->m_cellHeight;
+ }
+ return cellItem->implicitHeight();
+}
+
+void QQuickTableViewPrivate::calculateColumnWidthsAfterRebuilding()
+{
+ qreal prevColumnWidth = 0;
+ for (int column = loadedTable.left(); column <= loadedTable.right(); ++column) {
+ qreal columnWidth = 0;
+ for (int row = loadedTable.top(); row <= loadedTable.bottom(); ++row)
+ columnWidth = qMax(columnWidth, cellWidth(QPoint(column, row)));
+
+ if (columnWidth <= 0)
+ columnWidth = kDefaultColumnWidth;
+
+ if (columnWidth == prevColumnWidth)
+ continue;
+
+ columnWidths.append({column, columnWidth});
+ prevColumnWidth = columnWidth;
+ }
+
+ if (columnWidths.isEmpty()) {
+ // Add at least one column, wo we don't need
+ // to check if the vector is empty elsewhere.
+ columnWidths.append({0, 0});
+ }
+}
+
+void QQuickTableViewPrivate::calculateRowHeightsAfterRebuilding()
+{
+ qreal prevRowHeight = 0;
+ for (int row = loadedTable.top(); row <= loadedTable.bottom(); ++row) {
+ qreal rowHeight = 0;
+ for (int column = loadedTable.left(); column <= loadedTable.right(); ++column)
+ rowHeight = qMax(rowHeight, cellHeight(QPoint(column, row)));
+
+ if (rowHeight <= 0)
+ rowHeight = kDefaultRowHeight;
+
+ if (rowHeight == prevRowHeight)
+ continue;
+
+ rowHeights.append({row, rowHeight});
+ prevRowHeight = rowHeight;
+ }
+
+ if (rowHeights.isEmpty()) {
+ // Add at least one row, wo we don't need
+ // to check if the vector is empty elsewhere.
+ rowHeights.append({0, 0});
+ }
+}
+
+void QQuickTableViewPrivate::calculateColumnWidth(int column)
+{
+ if (column < columnWidths.last().index) {
+ // We only do the calculation once, and then stick with the size.
+ // See comments inside ColumnRowSize struct.
+ return;
+ }
+
+ qreal columnWidth = 0;
+ for (int row = loadedTable.top(); row <= loadedTable.bottom(); ++row)
+ columnWidth = qMax(columnWidth, cellWidth(QPoint(column, row)));
+
+ if (columnWidth <= 0)
+ columnWidth = kDefaultColumnWidth;
+
+ if (columnWidth == columnWidths.last().size)
+ return;
+
+ columnWidths.append({column, columnWidth});
+}
+
+void QQuickTableViewPrivate::calculateRowHeight(int row)
+{
+ if (row < rowHeights.last().index) {
+ // We only do the calculation once, and then stick with the size.
+ // See comments inside ColumnRowSize struct.
+ return;
+ }
+
+ qreal rowHeight = 0;
+ for (int column = loadedTable.left(); column <= loadedTable.right(); ++column)
+ rowHeight = qMax(rowHeight, cellHeight(QPoint(column, row)));
+
+ if (rowHeight <= 0)
+ rowHeight = kDefaultRowHeight;
+
+ if (rowHeight == rowHeights.last().size)
+ return;
+
+ rowHeights.append({row, rowHeight});
+}
+
+void QQuickTableViewPrivate::calculateEdgeSizeFromLoadRequest()
+{
+ if (tableRebuilding)
+ return;
+
+ switch (loadRequest.edge()) {
+ case Qt::LeftEdge:
+ case Qt::TopEdge:
+ // Flicking left or up through "never loaded" rows/columns is currently
+ // not supported. You always need to start loading the table from the beginning.
+ return;
+ case Qt::RightEdge:
+ if (tableSize.height() > 1)
+ calculateColumnWidth(loadedTable.right());
+ break;
+ case Qt::BottomEdge:
+ if (tableSize.width() > 1)
+ calculateRowHeight(loadedTable.bottom());
+ break;
+ default:
+ Q_TABLEVIEW_UNREACHABLE("This function should not be called when loading top-left item");
+ }
+}
+
+void QQuickTableViewPrivate::calculateTableSize()
+{
+ // tableSize is the same as row and column count, and will always
+ // be the same as the number of rows and columns in the model.
+ Q_Q(QQuickTableView);
+ QSize prevTableSize = tableSize;
+
+ if (delegateModel)
+ tableSize = QSize(delegateModel->columns(), delegateModel->rows());
+ else if (model)
+ tableSize = QSize(1, model->count());
+ else
+ tableSize = QSize(0, 0);
+
+ if (prevTableSize.width() != tableSize.width())
+ emit q->columnsChanged();
+ if (prevTableSize.height() != tableSize.height())
+ emit q->rowsChanged();
+}
+
+qreal QQuickTableViewPrivate::columnWidth(int column)
+{
+ if (!columnWidths.isEmpty()) {
+ // Find the first ColumnRowSize with a column before, or at, the given column
+ auto iter = std::upper_bound(columnWidths.constBegin(), columnWidths.constEnd(),
+ ColumnRowSize{column, -1}, ColumnRowSize::lessThan);
+
+ if (iter == columnWidths.constEnd()) {
+ // If the table is not a list, return the size
+ // of the last recorded ColumnRowSize.
+ if (tableSize.height() > 1)
+ return columnWidths.last().size;
+ } else {
+ // Check if we got an explicit assignment for this column
+ if (iter->index == column)
+ return iter->size;
+
+ // If the table is not a list, return the size of
+ // ColumnRowSize element found before column. Since there
+ // is always an element stored for column 0, this is safe.
+ // Otherwise we continue, and return the size of the delegate
+ // item at the given column instead.
+ if (tableSize.height() > 1)
+ return (iter - 1)->size;
+ }
+ }
+
+ // If we have an item loaded at column, return the width of the item.
+ if (column >= loadedTable.left() && column <= loadedTable.right())
+ return cellWidth(QPoint(column, loadedTable.top()));
+
+ return -1;
+}
+
+qreal QQuickTableViewPrivate::rowHeight(int row)
+{
+ if (!rowHeights.isEmpty()) {
+ // Find the ColumnRowSize assignment before, or at, row
+ auto iter = std::lower_bound(rowHeights.constBegin(), rowHeights.constEnd(),
+ ColumnRowSize{row, -1}, ColumnRowSize::lessThan);
+
+ if (iter == rowHeights.constEnd()) {
+ // If the table is not a list, return the size
+ // of the last recorded ColumnRowSize.
+ if (tableSize.width() > 1)
+ return rowHeights.last().size;
+ } else {
+ // Check if we got an explicit assignment for this row
+ if (iter->index == row)
+ return iter->size;
+
+ // If the table is not a list, return the size of
+ // ColumnRowSize element found before row. Since there
+ // is always an element stored for row 0, this is safe.
+ // Otherwise we continue, and return the size of the delegate
+ // item at the given row instead.
+ if (tableSize.width() > 1)
+ return (iter - 1)->size;
+ }
+ }
+
+ // If we have an item loaded at row, return the height of the item.
+ if (row >= loadedTable.top() && row <= loadedTable.bottom())
+ return cellHeight(QPoint(loadedTable.left(), row));
+
+ return -1;
+}
+
+void QQuickTableViewPrivate::relayoutTable()
+{
+ relayoutTableItems();
+ columnRowPositionsInvalid = false;
+
+ syncLoadedTableRectFromLoadedTable();
+ contentSizeBenchMarkPoint = QPoint(-1, -1);
+ updateContentWidth();
+ updateContentHeight();
+}
+
+void QQuickTableViewPrivate::relayoutTableItems()
+{
+ qCDebug(lcTableViewDelegateLifecycle);
+ columnRowPositionsInvalid = false;
+
+ qreal nextColumnX = loadedTableOuterRect.x();
+ qreal nextRowY = loadedTableOuterRect.y();
+
+ for (int column = loadedTable.left(); column <= loadedTable.right(); ++column) {
+ // Adjust the geometry of all cells in the current column
+ qreal width = columnWidth(column);
+ if (width <= 0)
+ width = kDefaultColumnWidth;
+
+ for (int row = loadedTable.top(); row <= loadedTable.bottom(); ++row) {
+ auto item = loadedTableItem(QPoint(column, row));
+ QRectF geometry = item->geometry();
+ geometry.moveLeft(nextColumnX);
+ geometry.setWidth(width);
+ item->setGeometry(geometry);
+ }
+
+ nextColumnX += width + cellSpacing.width();
+ }
+
+ for (int row = loadedTable.top(); row <= loadedTable.bottom(); ++row) {
+ // Adjust the geometry of all cells in the current row
+ qreal height = rowHeight(row);
+ if (height <= 0)
+ height = kDefaultRowHeight;
+
+ for (int column = loadedTable.left(); column <= loadedTable.right(); ++column) {
+ auto item = loadedTableItem(QPoint(column, row));
+ QRectF geometry = item->geometry();
+ geometry.moveTop(nextRowY);
+ geometry.setHeight(height);
+ item->setGeometry(geometry);
+ }
+
+ nextRowY += height + cellSpacing.height();
+ }
+
+ if (Q_UNLIKELY(lcTableViewDelegateLifecycle().isDebugEnabled())) {
+ for (int column = loadedTable.left(); column <= loadedTable.right(); ++column) {
+ for (int row = loadedTable.top(); row <= loadedTable.bottom(); ++row) {
+ QPoint cell = QPoint(column, row);
+ qCDebug(lcTableViewDelegateLifecycle()) << "relayout item:" << cell << loadedTableItem(cell)->geometry();
+ }
+ }
+ }
+}
+
+void QQuickTableViewPrivate::layoutVerticalEdge(Qt::Edge tableEdge)
+{
+ int column = (tableEdge == Qt::LeftEdge) ? loadedTable.left() : loadedTable.right();
+ QPoint neighbourDirection = (tableEdge == Qt::LeftEdge) ? kRight : kLeft;
+ qreal left = -1;
+
+ qreal width = columnWidth(column);
+ if (width <= 0)
+ width = kDefaultColumnWidth;
+
+ for (int row = loadedTable.top(); row <= loadedTable.bottom(); ++row) {
+ auto fxTableItem = loadedTableItem(QPoint(column, row));
+ auto const neighbourItem = itemNextTo(fxTableItem, neighbourDirection);
+
+ QRectF geometry = fxTableItem->geometry();
+ geometry.setWidth(width);
+ geometry.setHeight(neighbourItem->geometry().height());
+
+ if (left == -1) {
+ // left will be the same for all items in the
+ // column, so do the calculation once.
+ left = tableEdge == Qt::LeftEdge ?
+ neighbourItem->geometry().left() - cellSpacing.width() - geometry.width() :
+ neighbourItem->geometry().right() + cellSpacing.width();
+ }
+
+ geometry.moveLeft(left);
+ geometry.moveTop(neighbourItem->geometry().top());
+
+ fxTableItem->setGeometry(geometry);
+ fxTableItem->setVisible(true);
+
+ qCDebug(lcTableViewDelegateLifecycle()) << "layout item:" << QPoint(column, row) << fxTableItem->geometry();
+ }
+}
+
+void QQuickTableViewPrivate::layoutHorizontalEdge(Qt::Edge tableEdge)
+{
+ int row = (tableEdge == Qt::TopEdge) ? loadedTable.top() : loadedTable.bottom();
+ QPoint neighbourDirection = (tableEdge == Qt::TopEdge) ? kDown : kUp;
+ qreal top = -1;
+
+ qreal height = rowHeight(row);
+ if (height <= 0)
+ height = kDefaultRowHeight;
+
+ for (int column = loadedTable.left(); column <= loadedTable.right(); ++column) {
+ auto fxTableItem = loadedTableItem(QPoint(column, row));
+ auto const neighbourItem = itemNextTo(fxTableItem, neighbourDirection);
+
+ QRectF geometry = fxTableItem->geometry();
+ geometry.setWidth(neighbourItem->geometry().width());
+ geometry.setHeight(height);
+
+ if (top == -1) {
+ // top will be the same for all items in the
+ // row, so do the calculation once.
+ top = tableEdge == Qt::TopEdge ?
+ neighbourItem->geometry().top() - cellSpacing.height() - geometry.height() :
+ neighbourItem->geometry().bottom() + cellSpacing.height();
+ }
+
+ geometry.moveTop(top);
+ geometry.moveLeft(neighbourItem->geometry().left());
+
+ fxTableItem->setGeometry(geometry);
+ fxTableItem->setVisible(true);
+
+ qCDebug(lcTableViewDelegateLifecycle()) << "layout item:" << QPoint(column, row) << fxTableItem->geometry();
+ }
+}
+
+void QQuickTableViewPrivate::layoutTopLeftItem()
+{
+ // ###todo: support starting with other top-left items than 0,0
+ const QPoint cell = loadRequest.firstCell();
+ Q_TABLEVIEW_ASSERT(cell == QPoint(0, 0), loadRequest.toString());
+ auto topLeftItem = loadedTableItem(cell);
+ auto item = topLeftItem->item;
+
+ qreal width = cellWidth(cell);
+ qreal height = cellHeight(cell);
+ if (width <= 0)
+ width = kDefaultColumnWidth;
+ if (height <= 0)
+ height = kDefaultRowHeight;
+
+ item->setPosition(QPoint(tableMargins.left(), tableMargins.top()));
+ item->setSize(QSizeF(width, height));
+ topLeftItem->setVisible(true);
+ qCDebug(lcTableViewDelegateLifecycle) << "geometry:" << topLeftItem->geometry();
+}
+
+void QQuickTableViewPrivate::layoutTableEdgeFromLoadRequest()
+{
+ switch (loadRequest.edge()) {
+ case Qt::LeftEdge:
+ case Qt::RightEdge:
+ layoutVerticalEdge(loadRequest.edge());
+ break;
+ case Qt::TopEdge:
+ case Qt::BottomEdge:
+ layoutHorizontalEdge(loadRequest.edge());
+ break;
+ default:
+ layoutTopLeftItem();
+ break;
+ }
+}
+
+void QQuickTableViewPrivate::cancelLoadRequest()
+{
+ loadRequest.markAsDone();
+ model->cancel(modelIndexAtCell(loadRequest.currentCell()));
+
+ if (tableInvalid) {
+ // No reason to rollback already loaded edge items
+ // since we anyway are about to reload all items.
+ return;
+ }
+
+ if (loadRequest.atBeginning()) {
+ // No items have yet been loaded, so nothing to unload
+ return;
+ }
+
+ QLine rollbackItems;
+ rollbackItems.setP1(loadRequest.firstCell());
+ rollbackItems.setP2(loadRequest.previousCell());
+ qCDebug(lcTableViewDelegateLifecycle()) << "rollback:" << rollbackItems << tableLayoutToString();
+ unloadItems(rollbackItems);
+}
+
+void QQuickTableViewPrivate::processLoadRequest()
+{
+ Q_TABLEVIEW_ASSERT(loadRequest.isActive(), "");
+
+ while (loadRequest.hasCurrentCell()) {
+ QPoint cell = loadRequest.currentCell();
+ FxTableItem *fxTableItem = loadFxTableItem(cell, loadRequest.incubationMode());
+
+ if (!fxTableItem) {
+ // Requested item is not yet ready. Just leave, and wait for this
+ // function to be called again when the item is ready.
+ return;
+ }
+
+ loadedItems.insert(modelIndexAtCell(cell), fxTableItem);
+ loadRequest.moveToNextCell();
+ }
+
+ qCDebug(lcTableViewDelegateLifecycle()) << "all items loaded!";
+
+ syncLoadedTableFromLoadRequest();
+ calculateEdgeSizeFromLoadRequest();
+ layoutTableEdgeFromLoadRequest();
+
+ syncLoadedTableRectFromLoadedTable();
+ enforceFirstRowColumnAtOrigo();
+ updateContentWidth();
+ updateContentHeight();
+
+ loadRequest.markAsDone();
+ qCDebug(lcTableViewDelegateLifecycle()) << "request completed! Table:" << tableLayoutToString();
+}
+
+void QQuickTableViewPrivate::beginRebuildTable()
+{
+ qCDebug(lcTableViewDelegateLifecycle());
+ clear();
+ tableInvalid = false;
+ tableRebuilding = true;
+ calculateTableSize();
+ loadInitialTopLeftItem();
+ loadAndUnloadVisibleEdges();
+}
+
+void QQuickTableViewPrivate::endRebuildTable()
+{
+ tableRebuilding = false;
+
+ if (loadedItems.isEmpty())
+ return;
+
+ // We don't calculate row/column sizes for lists.
+ // Instead we we use the sizes of the items directly
+ // unless for explicit row/column size assignments.
+ columnWidths.clear();
+ rowHeights.clear();
+ if (tableSize.height() > 1)
+ calculateColumnWidthsAfterRebuilding();
+ if (tableSize.width() > 1)
+ calculateRowHeightsAfterRebuilding();
+
+ relayoutTable();
+ qCDebug(lcTableViewDelegateLifecycle()) << tableLayoutToString();
+}
+
+void QQuickTableViewPrivate::loadInitialTopLeftItem()
+{
+ Q_TABLEVIEW_ASSERT(loadedItems.isEmpty(), "");
+
+ if (tableSize.isEmpty())
+ return;
+
+ if (model->count() == 0)
+ return;
+
+ // Load top-left item. After loaded, loadItemsInsideRect() will take
+ // care of filling out the rest of the table.
+ loadRequest.begin(QPoint(0, 0), QQmlIncubator::AsynchronousIfNested);
+ processLoadRequest();
+}
+
+void QQuickTableViewPrivate::unloadEdge(Qt::Edge edge)
+{
+ unloadItems(rectangleEdge(loadedTable, edge));
+ loadedTable = expandedRect(loadedTable, edge, -1);
+ syncLoadedTableRectFromLoadedTable();
+ qCDebug(lcTableViewDelegateLifecycle) << tableLayoutToString();
+}
+
+void QQuickTableViewPrivate::loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMode incubationMode)
+{
+ QLine cellsToLoad = rectangleEdge(expandedRect(loadedTable, edge, 1), edge);
+ loadRequest.begin(cellsToLoad, edge, incubationMode);
+ processLoadRequest();
+}
+
+void QQuickTableViewPrivate::loadAndUnloadVisibleEdges()
+{
+ // Unload table edges that have been moved outside the visible part of the
+ // table (including buffer area), and load new edges that has been moved inside.
+ // Note: an important point is that we always keep the table rectangular
+ // and without holes to reduce complexity (we never leave the table in
+ // a half-loaded state, or keep track of multiple patches).
+ // We load only one edge (row or column) at a time. This is especially
+ // important when loading into the buffer, since we need to be able to
+ // cancel the buffering quickly if the user starts to flick, and then
+ // focus all further loading on the edges that are flicked into view.
+
+ if (loadRequest.isActive()) {
+ // Don't start loading more edges while we're
+ // already waiting for another one to load.
+ return;
+ }
+
+ if (loadedItems.isEmpty()) {
+ // We need at least the top-left item to be loaded before we can
+ // start loading edges around it. Not having a top-left item at
+ // this point means that the model is empty (or no delegate).
+ return;
+ }
+
+ const QRectF unloadRect = hasBufferedItems ? bufferRect() : viewportRect;
+ bool tableModified;
+
+ do {
+ tableModified = false;
+
+ if (Qt::Edge edge = nextEdgeToUnload(unloadRect)) {
+ tableModified = true;
+ unloadEdge(edge);
+ }
+
+ if (Qt::Edge edge = nextEdgeToLoad(viewportRect)) {
+ tableModified = true;
+ loadEdge(edge, QQmlIncubator::AsynchronousIfNested);
+ if (loadRequest.isActive())
+ return;
+ }
+ } while (tableModified);
+
+}
+
+void QQuickTableViewPrivate::loadBuffer()
+{
+ // Rather than making sure to stop the timer from all locations that can
+ // violate the "buffering allowed" state, we just check that we're in the
+ // right state here before we start buffering.
+ if (cacheBuffer <= 0 || loadRequest.isActive() || loadedItems.isEmpty())
+ return;
+
+ qCDebug(lcTableViewDelegateLifecycle());
+ const QRectF loadRect = bufferRect();
+ while (Qt::Edge edge = nextEdgeToLoad(loadRect)) {
+ loadEdge(edge, QQmlIncubator::Asynchronous);
+ if (loadRequest.isActive())
+ break;
+ }
+
+ hasBufferedItems = true;
+}
+
+void QQuickTableViewPrivate::unloadBuffer()
+{
+ if (!hasBufferedItems)
+ return;
+
+ qCDebug(lcTableViewDelegateLifecycle());
+ hasBufferedItems = false;
+ cacheBufferDelayTimer.stop();
+ if (loadRequest.isActive())
+ cancelLoadRequest();
+ while (Qt::Edge edge = nextEdgeToUnload(viewportRect))
+ unloadEdge(edge);
+}
+
+QRectF QQuickTableViewPrivate::bufferRect()
+{
+ return viewportRect.adjusted(-cacheBuffer, -cacheBuffer, cacheBuffer, cacheBuffer);
+}
+
+void QQuickTableViewPrivate::invalidateTable() {
+ tableInvalid = true;
+ if (loadRequest.isActive())
+ cancelLoadRequest();
+ q_func()->polish();
+}
+
+void QQuickTableViewPrivate::invalidateColumnRowPositions() {
+ columnRowPositionsInvalid = true;
+ q_func()->polish();
+}
+
+void QQuickTableViewPrivate::updatePolish()
+{
+ // Whenever something changes, e.g viewport moves, spacing is set to a
+ // new value, model changes etc, this function will end up being called. Here
+ // we check what needs to be done, and load/unload cells accordingly.
+ Q_Q(QQuickTableView);
+
+ if (loadRequest.isActive()) {
+ // We're currently loading items async to build a new edge in the table. We see the loading
+ // as an atomic operation, which means that we don't continue doing anything else until all
+ // items have been received and laid out. Note that updatePolish is then called once more
+ // after the loadRequest has completed to handle anything that might have occurred in-between.
+ return;
+ }
+
+ // viewportRect describes the part of the content view that is actually visible. Since a
+ // negative width/height can happen (e.g during start-up), we check for this to avoid rebuilding
+ // the table (and e.g calculate initial row/column sizes) based on a premature viewport rect.
+ viewportRect = QRectF(q->contentX(), q->contentY(), q->width(), q->height());
+ if (!viewportRect.isValid())
+ return;
+
+ if (tableInvalid) {
+ beginRebuildTable();
+ if (loadRequest.isActive())
+ return;
+ }
+
+ if (tableRebuilding)
+ endRebuildTable();
+
+ if (loadedItems.isEmpty()) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "no items loaded, meaning empty model or no delegate";
+ return;
+ }
+
+ if (columnRowPositionsInvalid)
+ relayoutTable();
+
+ if (hasBufferedItems && nextEdgeToLoad(viewportRect)) {
+ // We are about to load more edges, so trim down the table as much
+ // as possible to avoid loading cells that are outside the viewport.
+ unloadBuffer();
+ }
+
+ loadAndUnloadVisibleEdges();
+
+ if (loadRequest.isActive())
+ return;
+
+ if (cacheBuffer > 0) {
+ // When polish hasn't been called for a while (which means that the viewport
+ // rect hasn't changed), we start buffering items. We delay this operation by
+ // using a timer to increase performance (by not loading hidden items) while
+ // the user is flicking.
+ cacheBufferDelayTimer.start(kBufferTimerInterval);
+ }
+}
+
+void QQuickTableViewPrivate::createWrapperModel()
+{
+ Q_Q(QQuickTableView);
+
+ delegateModel = new QQmlDelegateModel(qmlContext(q), q);
+ if (q->isComponentComplete())
+ delegateModel->componentComplete();
+ model = delegateModel;
+}
+
+void QQuickTableViewPrivate::itemCreatedCallback(int modelIndex, QObject*)
+{
+ if (blockItemCreatedCallback)
+ return;
+
+ qCDebug(lcTableViewDelegateLifecycle) << "item done loading:"
+ << cellAtModelIndex(modelIndex);
+
+ // Since the item we waited for has finished incubating, we can
+ // continue with the load request. processLoadRequest will
+ // ask the model for the requested item once more, which will be
+ // quick since the model has cached it.
+ processLoadRequest();
+ loadAndUnloadVisibleEdges();
+ updatePolish();
+}
+
+void QQuickTableViewPrivate::initItemCallback(int modelIndex, QObject *object)
+{
+ Q_UNUSED(modelIndex);
+ auto attached = getAttachedObject(object);
+ if (!attached)
+ return;
+
+ attached->setTableView(q_func());
+}
+
+void QQuickTableViewPrivate::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
+{
+ Q_UNUSED(changeSet);
+ Q_UNUSED(reset);
+
+ // TODO: implement fine-grained support for model changes
+ invalidateTable();
+}
+
+QQuickTableView::QQuickTableView(QQuickItem *parent)
+ : QQuickFlickable(*(new QQuickTableViewPrivate), parent)
+{
+}
+
+int QQuickTableView::rows() const
+{
+ return d_func()->tableSize.height();
+}
+
+int QQuickTableView::columns() const
+{
+ return d_func()->tableSize.width();
+}
+
+qreal QQuickTableView::rowSpacing() const
+{
+ return d_func()->cellSpacing.height();
+}
+
+void QQuickTableView::setRowSpacing(qreal spacing)
+{
+ Q_D(QQuickTableView);
+ if (qFuzzyCompare(d->cellSpacing.height(), spacing))
+ return;
+
+ d->cellSpacing.setHeight(spacing);
+ d->invalidateColumnRowPositions();
+ emit rowSpacingChanged();
+}
+
+qreal QQuickTableView::columnSpacing() const
+{
+ return d_func()->cellSpacing.width();
+}
+
+void QQuickTableView::setColumnSpacing(qreal spacing)
+{
+ Q_D(QQuickTableView);
+ if (qFuzzyCompare(d->cellSpacing.width(), spacing))
+ return;
+
+ d->cellSpacing.setWidth(spacing);
+ d->invalidateColumnRowPositions();
+ emit columnSpacingChanged();
+}
+
+qreal QQuickTableView::topMargin() const
+{
+ return d_func()->tableMargins.top();
+}
+
+void QQuickTableView::setTopMargin(qreal margin)
+{
+ Q_D(QQuickTableView);
+ if (qt_is_nan(margin))
+ return;
+ if (qFuzzyCompare(d->tableMargins.top(), margin))
+ return;
+
+ d->tableMargins.setTop(margin);
+ d->invalidateColumnRowPositions();
+ emit topMarginChanged();
+}
+
+qreal QQuickTableView::bottomMargin() const
+{
+ return d_func()->tableMargins.bottom();
+}
+
+void QQuickTableView::setBottomMargin(qreal margin)
+{
+ Q_D(QQuickTableView);
+ if (qt_is_nan(margin))
+ return;
+ if (qFuzzyCompare(d->tableMargins.bottom(), margin))
+ return;
+
+ d->tableMargins.setBottom(margin);
+ d->invalidateColumnRowPositions();
+ emit bottomMarginChanged();
+}
+
+qreal QQuickTableView::leftMargin() const
+{
+ return d_func()->tableMargins.left();
+}
+
+void QQuickTableView::setLeftMargin(qreal margin)
+{
+ Q_D(QQuickTableView);
+ if (qt_is_nan(margin))
+ return;
+ if (qFuzzyCompare(d->tableMargins.left(), margin))
+ return;
+
+ d->tableMargins.setLeft(margin);
+ d->invalidateColumnRowPositions();
+ emit leftMarginChanged();
+}
+
+qreal QQuickTableView::rightMargin() const
+{
+ return d_func()->tableMargins.right();
+}
+
+void QQuickTableView::setRightMargin(qreal margin)
+{
+ Q_D(QQuickTableView);
+ if (qt_is_nan(margin))
+ return;
+ if (qFuzzyCompare(d->tableMargins.right(), margin))
+ return;
+
+ d->tableMargins.setRight(margin);
+ d->invalidateColumnRowPositions();
+ emit rightMarginChanged();
+}
+
+int QQuickTableView::cacheBuffer() const
+{
+ return d_func()->cacheBuffer;
+}
+
+void QQuickTableView::setCacheBuffer(int newBuffer)
+{
+ Q_D(QQuickTableView);
+ if (d->cacheBuffer == newBuffer || newBuffer < 0)
+ return;
+
+ d->cacheBuffer = newBuffer;
+
+ if (newBuffer == 0)
+ d->unloadBuffer();
+
+ emit cacheBufferChanged();
+ polish();
+}
+
+QVariant QQuickTableView::model() const
+{
+ return d_func()->modelVariant;
+}
+
+void QQuickTableView::setModel(const QVariant &newModel)
+{
+ Q_D(QQuickTableView);
+
+ d->modelVariant = newModel;
+ QVariant effectiveModelVariant = d->modelVariant;
+ if (effectiveModelVariant.userType() == qMetaTypeId<QJSValue>())
+ effectiveModelVariant = effectiveModelVariant.value<QJSValue>().toVariant();
+
+ if (d->model) {
+ QObjectPrivate::disconnect(d->model, &QQmlInstanceModel::createdItem, d, &QQuickTableViewPrivate::itemCreatedCallback);
+ QObjectPrivate::disconnect(d->model, &QQmlInstanceModel::initItem, d, &QQuickTableViewPrivate::initItemCallback);
+ QObjectPrivate::disconnect(d->model, &QQmlInstanceModel::modelUpdated, d, &QQuickTableViewPrivate::modelUpdated);
+ }
+
+ const auto instanceModel = qobject_cast<QQmlInstanceModel *>(qvariant_cast<QObject*>(effectiveModelVariant));
+
+ if (instanceModel) {
+ if (d->delegateModel)
+ delete d->delegateModel;
+ d->model = instanceModel;
+ d->delegateModel = qmlobject_cast<QQmlDelegateModel *>(instanceModel);
+ } else {
+ if (!d->delegateModel)
+ d->createWrapperModel();
+ QQmlDelegateModelPrivate::get(d->delegateModel)->m_useFirstColumnOnly = false;
+ d->delegateModel->setModel(effectiveModelVariant);
+ }
+
+ Q_ASSERT(d->model);
+ QObjectPrivate::connect(d->model, &QQmlInstanceModel::createdItem, d, &QQuickTableViewPrivate::itemCreatedCallback);
+ QObjectPrivate::connect(d->model, &QQmlInstanceModel::initItem, d, &QQuickTableViewPrivate::initItemCallback);
+ QObjectPrivate::connect(d->model, &QQmlInstanceModel::modelUpdated, d, &QQuickTableViewPrivate::modelUpdated);
+
+ d->invalidateTable();
+
+ emit modelChanged();
+}
+
+QQmlComponent *QQuickTableView::delegate() const
+{
+ Q_D(const QQuickTableView);
+ if (d->delegateModel)
+ return d->delegateModel->delegate();
+
+ return nullptr;
+}
+
+void QQuickTableView::setDelegate(QQmlComponent *newDelegate)
+{
+ Q_D(QQuickTableView);
+ if (newDelegate == delegate())
+ return;
+
+ if (!d->delegateModel)
+ d->createWrapperModel();
+
+ d->delegateModel->setDelegate(newDelegate);
+ d->invalidateTable();
+
+ emit delegateChanged();
+}
+
+QQuickTableViewAttached *QQuickTableView::qmlAttachedProperties(QObject *obj)
+{
+ return new QQuickTableViewAttached(obj);
+}
+
+void QQuickTableView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ Q_D(QQuickTableView);
+ QQuickFlickable::geometryChanged(newGeometry, oldGeometry);
+ // We update the viewport rect from within updatePolish to
+ // ensure that we update when we're ready to update, and not
+ // while we're in the middle of loading/unloading edges.
+ d->updatePolish();
+}
+
+void QQuickTableView::viewportMoved(Qt::Orientations orientation)
+{
+ Q_D(QQuickTableView);
+ QQuickFlickable::viewportMoved(orientation);
+ // We update the viewport rect from within updatePolish to
+ // ensure that we update when we're ready to update, and not
+ // while we're in the middle of loading/unloading edges.
+ d->updatePolish();
+}
+
+void QQuickTableView::componentComplete()
+{
+ Q_D(QQuickTableView);
+
+ if (!d->model)
+ setModel(QVariant());
+
+ if (d->delegateModel)
+ d->delegateModel->componentComplete();
+
+ QQuickFlickable::componentComplete();
+}
+
+#include "moc_qquicktableview_p.cpp"
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquicktableview_p.h b/src/quick/items/qquicktableview_p.h
new file mode 100644
index 0000000000..d2c3f17bc4
--- /dev/null
+++ b/src/quick/items/qquicktableview_p.h
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKTABLEVIEW_P_H
+#define QQUICKTABLEVIEW_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qpointer.h>
+#include <QtQuick/private/qtquickglobal_p.h>
+#include <QtQuick/private/qquickflickable_p.h>
+#include <QtQml/private/qqmlnullablevalue_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickTableViewAttached;
+class QQuickTableViewPrivate;
+class QQmlChangeSet;
+
+class Q_QUICK_PRIVATE_EXPORT QQuickTableView : public QQuickFlickable
+{
+ Q_OBJECT
+
+ Q_PROPERTY(int rows READ rows NOTIFY rowsChanged)
+ Q_PROPERTY(int columns READ columns NOTIFY columnsChanged)
+ Q_PROPERTY(qreal rowSpacing READ rowSpacing WRITE setRowSpacing NOTIFY rowSpacingChanged)
+ Q_PROPERTY(qreal columnSpacing READ columnSpacing WRITE setColumnSpacing NOTIFY columnSpacingChanged)
+ Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin NOTIFY topMarginChanged)
+ Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged)
+ Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged)
+ Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin NOTIFY rightMarginChanged)
+ Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged)
+
+ Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged)
+ Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+
+public:
+ QQuickTableView(QQuickItem *parent = nullptr);
+
+ int rows() const;
+ int columns() const;
+
+ qreal rowSpacing() const;
+ void setRowSpacing(qreal spacing);
+
+ qreal columnSpacing() const;
+ void setColumnSpacing(qreal spacing);
+
+ qreal topMargin() const;
+ void setTopMargin(qreal margin);
+
+ qreal bottomMargin() const;
+ void setBottomMargin(qreal margin);
+
+ qreal leftMargin() const;
+ void setLeftMargin(qreal margin);
+
+ qreal rightMargin() const;
+ void setRightMargin(qreal margin);
+
+ int cacheBuffer() const;
+ void setCacheBuffer(int newBuffer);
+
+ QVariant model() const;
+ void setModel(const QVariant &newModel);
+
+ QQmlComponent *delegate() const;
+ void setDelegate(QQmlComponent *);
+
+ static QQuickTableViewAttached *qmlAttachedProperties(QObject *);
+
+Q_SIGNALS:
+ void rowsChanged();
+ void columnsChanged();
+ void rowSpacingChanged();
+ void columnSpacingChanged();
+ void topMarginChanged();
+ void bottomMarginChanged();
+ void leftMarginChanged();
+ void rightMarginChanged();
+ void cacheBufferChanged();
+ void modelChanged();
+ void delegateChanged();
+
+protected:
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+ void viewportMoved(Qt::Orientations orientation) override;
+ void componentComplete() override;
+
+private:
+ Q_DISABLE_COPY(QQuickTableView)
+ Q_DECLARE_PRIVATE(QQuickTableView)
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickTableViewAttached : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QQuickTableView *tableView READ tableView NOTIFY tableViewChanged)
+ Q_PROPERTY(qreal cellWidth READ cellWidth WRITE setCellWidth NOTIFY cellWidthChanged)
+ Q_PROPERTY(qreal cellHeight READ cellHeight WRITE setCellHeight NOTIFY cellHeightChanged)
+
+public:
+ QQuickTableViewAttached(QObject *parent)
+ : QObject(parent) {}
+
+ QQuickTableView *tableView() const { return m_tableview; }
+ void setTableView(QQuickTableView *newTableView) {
+ if (newTableView == m_tableview)
+ return;
+ m_tableview = newTableView;
+ Q_EMIT tableViewChanged();
+ }
+
+ qreal cellWidth() const { return m_cellWidth; }
+ void setCellWidth(qreal newWidth) {
+ if (newWidth == m_cellWidth)
+ return;
+ m_cellWidth = newWidth;
+ Q_EMIT cellWidthChanged();
+ }
+
+ qreal cellHeight() const { return m_cellHeight; }
+ void setCellHeight(qreal newHeight) {
+ if (newHeight == m_cellHeight)
+ return;
+ m_cellHeight = newHeight;
+ Q_EMIT cellHeightChanged();
+ }
+
+Q_SIGNALS:
+ void tableViewChanged();
+ void cellWidthChanged();
+ void cellHeightChanged();
+
+private:
+ QPointer<QQuickTableView> m_tableview;
+ QQmlNullableValue<qreal> m_cellWidth;
+ QQmlNullableValue<qreal> m_cellHeight;
+
+ friend class QQuickTableViewPrivate;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickTableView)
+QML_DECLARE_TYPEINFO(QQuickTableView, QML_HAS_ATTACHED_PROPERTIES)
+
+#endif // QQUICKTABLEVIEW_P_H
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h
new file mode 100644
index 0000000000..9376ad386c
--- /dev/null
+++ b/src/quick/items/qquicktableview_p_p.h
@@ -0,0 +1,342 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquicktableview_p.h"
+
+#include <QtCore/qtimer.h>
+#include <QtQml/private/qqmldelegatemodel_p.h>
+#include <QtQml/private/qqmlincubator_p.h>
+#include <QtQml/private/qqmlchangeset_p.h>
+#include <QtQml/qqmlinfo.h>
+
+#include <QtQuick/private/qquickflickable_p_p.h>
+#include <QtQuick/private/qquickitemviewfxitem_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcTableViewDelegateLifecycle)
+
+static const int kDefaultCacheBuffer = 300;
+static const qreal kDefaultRowHeight = 50;
+static const qreal kDefaultColumnWidth = 50;
+
+class FxTableItem;
+
+class Q_QML_AUTOTEST_EXPORT QQuickTableViewPrivate : public QQuickFlickablePrivate
+{
+ Q_DECLARE_PUBLIC(QQuickTableView)
+
+public:
+ class TableEdgeLoadRequest
+ {
+ // Whenever we need to load new rows or columns in the
+ // table, we fill out a TableEdgeLoadRequest.
+ // TableEdgeLoadRequest is just a struct that keeps track
+ // of which cells that needs to be loaded, and which cell
+ // the table is currently loading. The loading itself is
+ // done by QQuickTableView.
+
+ public:
+ void begin(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode)
+ {
+ Q_ASSERT(!active);
+ active = true;
+ tableEdge = Qt::Edge(0);
+ tableCells = QLine(cell, cell);
+ mode = incubationMode;
+ cellCount = 1;
+ currentIndex = 0;
+ qCDebug(lcTableViewDelegateLifecycle()) << "begin top-left:" << toString();
+ }
+
+ void begin(const QLine cellsToLoad, Qt::Edge edgeToLoad, QQmlIncubator::IncubationMode incubationMode)
+ {
+ Q_ASSERT(!active);
+ active = true;
+ tableEdge = edgeToLoad;
+ tableCells = cellsToLoad;
+ mode = incubationMode;
+ cellCount = tableCells.x2() - tableCells.x1() + tableCells.y2() - tableCells.y1() + 1;
+ currentIndex = 0;
+ qCDebug(lcTableViewDelegateLifecycle()) << "begin:" << toString();
+ }
+
+ inline void markAsDone() { active = false; }
+ inline bool isActive() { return active; }
+
+ inline QPoint firstCell() { return tableCells.p1(); }
+ inline QPoint lastCell() { return tableCells.p2(); }
+ inline QPoint currentCell() { return cellAt(currentIndex); }
+ inline QPoint previousCell() { return cellAt(currentIndex - 1); }
+
+ inline bool atBeginning() { return currentIndex == 0; }
+ inline bool hasCurrentCell() { return currentIndex < cellCount; }
+ inline void moveToNextCell() { ++currentIndex; }
+
+ inline Qt::Edge edge() { return tableEdge; }
+ inline QQmlIncubator::IncubationMode incubationMode() { return mode; }
+
+ QString toString()
+ {
+ QString str;
+ QDebug dbg(&str);
+ dbg.nospace() << "TableSectionLoadRequest(" << "edge:"
+ << tableEdge << " cells:" << tableCells << " incubation:";
+
+ switch (mode) {
+ case QQmlIncubator::Asynchronous:
+ dbg << "Asynchronous";
+ break;
+ case QQmlIncubator::AsynchronousIfNested:
+ dbg << "AsynchronousIfNested";
+ break;
+ case QQmlIncubator::Synchronous:
+ dbg << "Synchronous";
+ break;
+ }
+
+ return str;
+ }
+
+ private:
+ Qt::Edge tableEdge = Qt::Edge(0);
+ QLine tableCells;
+ int currentIndex = 0;
+ int cellCount = 0;
+ bool active = false;
+ QQmlIncubator::IncubationMode mode = QQmlIncubator::AsynchronousIfNested;
+
+ QPoint cellAt(int index)
+ {
+ int x = tableCells.p1().x() + (tableCells.dx() ? index : 0);
+ int y = tableCells.p1().y() + (tableCells.dy() ? index : 0);
+ return QPoint(x, y);
+ }
+ };
+
+ struct ColumnRowSize
+ {
+ // ColumnRowSize is a helper class for storing row heights
+ // and column widths. We calculate the average width of a column
+ // the first time it's scrolled into view based on the size of
+ // the loaded items in the new column. Since we never load more items
+ // that what fits inside the viewport (cachebuffer aside), this calculation
+ // would be different depending on which row you were at when the column
+ // was scrolling in. To avoid that a column resizes when it's scrolled
+ // in and out and in again, we store its width. But to avoid storing
+ // the width for all columns, we choose to only store the width if it
+ // differs from the column(s) to the left. The same logic applies for row heights.
+ // 'index' translates to either 'row' or 'column'.
+ int index;
+ qreal size;
+
+ static bool lessThan(const ColumnRowSize& a, const ColumnRowSize& b)
+ {
+ return a.index < b.index;
+ }
+ };
+
+public:
+ QQuickTableViewPrivate();
+ ~QQuickTableViewPrivate() override;
+
+ static inline QQuickTableViewPrivate *get(QQuickTableView *q) { return q->d_func(); }
+
+ void updatePolish() override;
+
+public:
+ QHash<int, FxTableItem *> loadedItems;
+
+ // model, delegateModel and modelVariant all points to the same model. modelVariant
+ // is the model assigned by the user. And delegateModel is the wrapper model we create
+ // around it. But if the model is an instance model directly, we cannot wrap it, so
+ // we need a pointer for that case as well.
+ QQmlInstanceModel* model = nullptr;
+ QPointer<QQmlDelegateModel> delegateModel = nullptr;
+ QVariant modelVariant;
+
+ // loadedTable describes the table cells that are currently loaded (from top left
+ // row/column to bottom right row/column). loadedTableOuterRect describes the actual
+ // pixels that those cells cover, and is matched agains the viewport to determine when
+ // we need to fill up with more rows/columns. loadedTableInnerRect describes the pixels
+ // that the loaded table covers if you remove one row/column on each side of the table, and
+ // is used to determine rows/columns that are no longer visible and can be unloaded.
+ QRect loadedTable;
+ QRectF loadedTableOuterRect;
+ QRectF loadedTableInnerRect;
+
+ QRectF viewportRect = QRectF(0, 0, -1, -1);
+
+ QSize tableSize;
+
+ TableEdgeLoadRequest loadRequest;
+
+ QPoint contentSizeBenchMarkPoint = QPoint(-1, -1);
+ QSizeF cellSpacing;
+ QMarginsF tableMargins;
+
+ int cacheBuffer = kDefaultCacheBuffer;
+ QTimer cacheBufferDelayTimer;
+ bool hasBufferedItems = false;
+
+ bool blockItemCreatedCallback = false;
+ bool tableInvalid = false;
+ bool tableRebuilding = false;
+ bool columnRowPositionsInvalid = false;
+
+ QVector<ColumnRowSize> columnWidths;
+ QVector<ColumnRowSize> rowHeights;
+
+ const static QPoint kLeft;
+ const static QPoint kRight;
+ const static QPoint kUp;
+ const static QPoint kDown;
+
+#ifdef QT_DEBUG
+ QString forcedIncubationMode = qEnvironmentVariable("QT_TABLEVIEW_INCUBATION_MODE");
+#endif
+
+public:
+ QQuickTableViewAttached *getAttachedObject(const QObject *object) const;
+
+ int modelIndexAtCell(const QPoint &cell) const;
+ QPoint cellAtModelIndex(int modelIndex) const;
+
+ void calculateColumnWidthsAfterRebuilding();
+ void calculateRowHeightsAfterRebuilding();
+ void calculateColumnWidth(int column);
+ void calculateRowHeight(int row);
+ void calculateEdgeSizeFromLoadRequest();
+ void calculateTableSize();
+
+ qreal columnWidth(int column);
+ qreal rowHeight(int row);
+
+ void relayoutTable();
+ void relayoutTableItems();
+
+ void layoutVerticalEdge(Qt::Edge tableEdge);
+ void layoutHorizontalEdge(Qt::Edge tableEdge);
+ void layoutTopLeftItem();
+ void layoutTableEdgeFromLoadRequest();
+
+ void updateContentWidth();
+ void updateContentHeight();
+
+ void enforceFirstRowColumnAtOrigo();
+ void syncLoadedTableRectFromLoadedTable();
+ void syncLoadedTableFromLoadRequest();
+
+ bool canLoadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const;
+ bool canUnloadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const;
+ Qt::Edge nextEdgeToLoad(const QRectF rect);
+ Qt::Edge nextEdgeToUnload(const QRectF rect);
+
+ qreal cellWidth(const QPoint &cell);
+ qreal cellHeight(const QPoint &cell);
+
+ FxTableItem *loadedTableItem(const QPoint &cell) const;
+ FxTableItem *itemNextTo(const FxTableItem *fxTableItem, const QPoint &direction) const;
+ FxTableItem *createFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode);
+ FxTableItem *loadFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode);
+
+ void releaseItem(FxTableItem *fxTableItem);
+ void releaseLoadedItems();
+ void clear();
+
+ void unloadItem(const QPoint &cell);
+ void unloadItems(const QLine &items);
+
+ void loadInitialTopLeftItem();
+ void loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMode incubationMode);
+ void unloadEdge(Qt::Edge edge);
+ void loadAndUnloadVisibleEdges();
+ void cancelLoadRequest();
+ void processLoadRequest();
+ void beginRebuildTable();
+ void endRebuildTable();
+
+ void loadBuffer();
+ void unloadBuffer();
+ QRectF bufferRect();
+
+ void invalidateTable();
+ void invalidateColumnRowPositions();
+
+ void createWrapperModel();
+
+ void initItemCallback(int modelIndex, QObject *item);
+ void itemCreatedCallback(int modelIndex, QObject *object);
+ void modelUpdated(const QQmlChangeSet &changeSet, bool reset);
+
+ inline QString tableLayoutToString() const;
+ void dumpTable() const;
+};
+
+class FxTableItem : public QQuickItemViewFxItem
+{
+public:
+ FxTableItem(QQuickItem *item, QQuickTableView *table, bool own)
+ : QQuickItemViewFxItem(item, own, QQuickTableViewPrivate::get(table))
+ {
+ }
+
+ qreal position() const override { return 0; }
+ qreal endPosition() const override { return 0; }
+ qreal size() const override { return 0; }
+ qreal sectionSize() const override { return 0; }
+ bool contains(qreal, qreal) const override { return false; }
+
+ QPoint cell;
+};
+
+Q_DECLARE_TYPEINFO(QQuickTableViewPrivate::ColumnRowSize, Q_PRIMITIVE_TYPE);
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index 76720e5c5d..3c260165f7 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -88,6 +88,7 @@ QQuickTextPrivate::QQuickTextPrivate()
, truncated(false), hAlignImplicit(true), rightToLeftText(false)
, layoutTextElided(false), textHasChanged(true), needToUpdateLayout(false), formatModifiesFontSize(false)
, polishSize(false)
+ , updateSizeRecursionGuard(false)
{
implicitAntialiasing = true;
}
@@ -442,6 +443,7 @@ void QQuickTextPrivate::updateSize()
//### need to confirm cost of always setting these for richText
internalWidthUpdate = true;
+ qreal oldWidth = q->width();
qreal iWidth = -1;
if (!q->widthValid())
iWidth = size.width();
@@ -449,24 +451,35 @@ void QQuickTextPrivate::updateSize()
q->setImplicitSize(iWidth + hPadding, size.height() + vPadding);
internalWidthUpdate = false;
- if (iWidth == -1)
- q->setImplicitHeight(size.height() + vPadding);
-
- QTextBlock firstBlock = extra->doc->firstBlock();
- while (firstBlock.layout()->lineCount() == 0)
- firstBlock = firstBlock.next();
-
- QTextBlock lastBlock = extra->doc->lastBlock();
- while (lastBlock.layout()->lineCount() == 0)
- lastBlock = lastBlock.previous();
-
- if (firstBlock.lineCount() > 0 && lastBlock.lineCount() > 0) {
- QTextLine firstLine = firstBlock.layout()->lineAt(0);
- QTextLine lastLine = lastBlock.layout()->lineAt(lastBlock.layout()->lineCount() - 1);
- advance = QSizeF(lastLine.horizontalAdvance(),
- (lastLine.y() + lastBlock.layout()->position().y()) - (firstLine.y() + firstBlock.layout()->position().y()));
+ // If the implicit width update caused a recursive change of the width,
+ // we will have skipped integral parts of the layout due to the
+ // internalWidthUpdate recursion guard. To make sure everything is up
+ // to date, we need to run a second pass over the layout when updateSize()
+ // is done.
+ if (!qFuzzyCompare(q->width(), oldWidth) && !updateSizeRecursionGuard) {
+ updateSizeRecursionGuard = true;
+ updateSize();
+ updateSizeRecursionGuard = false;
} else {
- advance = QSizeF();
+ if (iWidth == -1)
+ q->setImplicitHeight(size.height() + vPadding);
+
+ QTextBlock firstBlock = extra->doc->firstBlock();
+ while (firstBlock.layout()->lineCount() == 0)
+ firstBlock = firstBlock.next();
+
+ QTextBlock lastBlock = extra->doc->lastBlock();
+ while (lastBlock.layout()->lineCount() == 0)
+ lastBlock = lastBlock.previous();
+
+ if (firstBlock.lineCount() > 0 && lastBlock.lineCount() > 0) {
+ QTextLine firstLine = firstBlock.layout()->lineAt(0);
+ QTextLine lastLine = lastBlock.layout()->lineAt(lastBlock.layout()->lineCount() - 1);
+ advance = QSizeF(lastLine.horizontalAdvance(),
+ (lastLine.y() + lastBlock.layout()->position().y()) - (firstLine.y() + firstBlock.layout()->position().y()));
+ } else {
+ advance = QSizeF();
+ }
}
}
diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h
index b0b1492d57..fd26d966c8 100644
--- a/src/quick/items/qquicktext_p_p.h
+++ b/src/quick/items/qquicktext_p_p.h
@@ -174,6 +174,7 @@ public:
bool needToUpdateLayout:1;
bool formatModifiesFontSize:1;
bool polishSize:1; // Workaround for problem with polish called after updateSize (QTBUG-42636)
+ bool updateSizeRecursionGuard:1;
static const QChar elideChar;
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index d9db8b56b4..f921e3962b 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -46,6 +46,7 @@
#include "qquickevents_p_p.h"
#include <private/qquickdrag_p.h>
+#include <private/qquickhoverhandler_p.h>
#include <private/qquickpointerhandler_p.h>
#include <QtQuick/private/qsgrenderer_p.h>
@@ -88,6 +89,8 @@ Q_LOGGING_CATEGORY(DBG_TOUCH, "qt.quick.touch")
Q_LOGGING_CATEGORY(DBG_TOUCH_TARGET, "qt.quick.touch.target")
Q_LOGGING_CATEGORY(DBG_MOUSE, "qt.quick.mouse")
Q_LOGGING_CATEGORY(DBG_MOUSE_TARGET, "qt.quick.mouse.target")
+Q_LOGGING_CATEGORY(lcWheelTarget, "qt.quick.wheel.target")
+Q_LOGGING_CATEGORY(lcGestureTarget, "qt.quick.gesture.target")
Q_LOGGING_CATEGORY(DBG_HOVER_TRACE, "qt.quick.hover.trace")
Q_LOGGING_CATEGORY(DBG_FOCUS, "qt.quick.focus")
Q_LOGGING_CATEGORY(DBG_DIRTY, "qt.quick.dirty")
@@ -256,14 +259,16 @@ void QQuickWindow::hideEvent(QHideEvent *)
void QQuickWindow::focusOutEvent(QFocusEvent *ev)
{
Q_D(QQuickWindow);
- d->contentItem->setFocus(false, ev->reason());
+ if (d->contentItem)
+ d->contentItem->setFocus(false, ev->reason());
}
/*! \reimp */
void QQuickWindow::focusInEvent(QFocusEvent *ev)
{
Q_D(QQuickWindow);
- d->contentItem->setFocus(true, ev->reason());
+ if (d->contentItem)
+ d->contentItem->setFocus(true, ev->reason());
d->updateFocusItemTransform();
}
@@ -1315,7 +1320,9 @@ QQuickWindow::~QQuickWindow()
#if QT_CONFIG(draganddrop)
delete d->dragGrabber; d->dragGrabber = nullptr;
#endif
- delete d->contentItem; d->contentItem = nullptr;
+ QQuickRootItem *root = d->contentItem;
+ d->contentItem = nullptr;
+ delete root;
qDeleteAll(d->pointerEventInstances);
d->pointerEventInstances.clear();
@@ -1540,8 +1547,20 @@ bool QQuickWindowPrivate::clearHover(ulong timestamp)
QPointF pos = q->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
bool accepted = false;
- for (QQuickItem* item : qAsConst(hoverItems))
+ for (QQuickItem* item : qAsConst(hoverItems)) {
accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), timestamp, true) || accepted;
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ if (itemPrivate->hasPointerHandlers()) {
+ pos = q->mapFromGlobal(QCursor::pos());
+ QQuickPointerEvent *pointerEvent = pointerEventInstance(QQuickPointerDevice::genericMouseDevice(), QEvent::MouseMove);
+ pointerEvent->point(0)->reset(Qt::TouchPointMoved, pos, quint64(1) << 24 /* mouse has device ID 1 */, timestamp, QVector2D());
+ pointerEvent->point(0)->setAccepted(true);
+ pointerEvent->localize(item);
+ for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers)
+ if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h))
+ hh->handlePointerEvent(pointerEvent);
+ }
+ }
hoverItems.clear();
return accepted;
}
@@ -1571,6 +1590,8 @@ bool QQuickWindow::event(QEvent *e)
return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(e));
break;
case QEvent::Enter: {
+ if (!d->contentItem)
+ return false;
QEnterEvent *enter = static_cast<QEnterEvent*>(e);
bool accepted = enter->isAccepted();
bool delivered = d->deliverHoverEvent(d->contentItem, enter->windowPos(), d->lastMousePosition,
@@ -1592,7 +1613,8 @@ bool QQuickWindow::event(QEvent *e)
break;
#endif
case QEvent::WindowDeactivate:
- contentItem()->windowDeactivateEvent();
+ if (d->contentItem)
+ d->contentItem->windowDeactivateEvent();
break;
case QEvent::Close: {
// TOOD Qt 6 (binary incompatible)
@@ -1617,7 +1639,7 @@ bool QQuickWindow::event(QEvent *e)
}
#if QT_CONFIG(gestures)
case QEvent::NativeGesture:
- d->deliverNativeGestureEvent(d->contentItem, static_cast<QNativeGestureEvent*>(e));
+ d->deliverSinglePointEventUntilAccepted(d->pointerEventInstance(e));
break;
#endif
case QEvent::ShortcutOverride:
@@ -1684,6 +1706,7 @@ void QQuickWindowPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QQuic
{
const QVector<QQuickPointerHandler *> &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets();
QVarLengthArray<QPair<QQuickItem *, bool>, 4> sendFilteredPointerEventResult;
+ hasFiltered.clear();
for (auto handler : passiveGrabbers) {
// a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically
if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) {
@@ -1720,6 +1743,7 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven
if (point->exclusiveGrabber()) {
if (auto grabber = point->grabberItem()) {
bool handled = false;
+ hasFiltered.clear();
if (sendFilteredPointerEvent(pointerEvent, grabber))
handled = true;
// if the grabber is an Item:
@@ -1748,6 +1772,7 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven
// if the grabber is not an Item, it must be a PointerHandler
auto handler = point->grabberPointerHandler();
pointerEvent->localize(handler->parentItem());
+ hasFiltered.clear();
if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem()))
handler->handlePointerEvent(pointerEvent);
if (mouseIsReleased)
@@ -1767,12 +1792,13 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven
// If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order
if (!pointerEvent->allPointsGrabbed() && pointerEvent->buttons()) {
- QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point->scenePosition(), false, false);
+ QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point, false, false);
for (QQuickItem *item : targetItems) {
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
if (!itemPrivate->extra.isAllocated() || itemPrivate->extra->pointerHandlers.isEmpty())
continue;
pointerEvent->localize(item);
+ hasFiltered.clear();
if (!sendFilteredPointerEvent(pointerEvent, item)) {
if (itemPrivate->handlePointerEvent(pointerEvent, true)) // avoid re-delivering to grabbers
delivered = true;
@@ -1834,6 +1860,16 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
}
}
+ if (itemPrivate->hasPointerHandlers()) {
+ QQuickPointerEvent *pointerEvent = pointerEventInstance(QQuickPointerDevice::genericMouseDevice(), QEvent::MouseMove);
+ pointerEvent->point(0)->reset(Qt::TouchPointMoved, scenePos, quint64(1) << 24 /* mouse has device ID 1 */, timestamp, QVector2D());
+ pointerEvent->point(0)->setAccepted(true);
+ pointerEvent->localize(item);
+ for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers)
+ if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h))
+ hh->handlePointerEvent(pointerEvent);
+ }
+
if (itemPrivate->hoverEnabled) {
QPointF p = item->mapFromScene(scenePos);
if (item->contains(p)) {
@@ -1886,43 +1922,57 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
return false;
}
-#if QT_CONFIG(wheelevent)
-bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event)
+// Simple delivery of non-mouse, non-touch Pointer Events: visit the items and handlers
+// in the usual reverse-paint-order until propagation is stopped
+bool QQuickWindowPrivate::deliverSinglePointEventUntilAccepted(QQuickPointerEvent *event)
{
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ Q_ASSERT(event->pointCount() == 1);
+ QQuickEventPoint *point = event->point(0);
+ QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point, false, false);
- if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
- QPointF p = item->mapFromScene(event->posF());
- if (!item->contains(p))
- return false;
- }
-
- QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
- for (int ii = children.count() - 1; ii >= 0; --ii) {
- QQuickItem *child = children.at(ii);
- if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
- continue;
- if (deliverWheelEvent(child, event))
- return true;
- }
-
- QPointF p = item->mapFromScene(event->posF());
-
- if (item->contains(p)) {
- QWheelEvent wheel(p, event->globalPosF(), event->pixelDelta(), event->angleDelta(), event->delta(),
- event->orientation(), event->buttons(), event->modifiers(), event->phase(), event->source(), event->inverted());
- wheel.setTimestamp(event->timestamp());
- wheel.accept();
- QCoreApplication::sendEvent(item, &wheel);
- if (wheel.isAccepted()) {
- event->accept();
+ for (QQuickItem *item : targetItems) {
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ event->localize(item);
+ // Let Pointer Handlers have the first shot
+ itemPrivate->handlePointerEvent(event);
+ if (point->isAccepted())
return true;
+ QPointF g = item->window()->mapToGlobal(point->scenePosition().toPoint());
+#if QT_CONFIG(wheelevent)
+ // Let the Item have a chance to handle it
+ if (QQuickPointerScrollEvent *pse = event->asPointerScrollEvent()) {
+ QWheelEvent wheel(point->position(), g, pse->pixelDelta().toPoint(), pse->angleDelta().toPoint(),
+ pse->buttons(), pse->modifiers(), pse->phase(),
+ pse->isInverted(), pse->synthSource());
+ wheel.setTimestamp(pse->timestamp());
+ wheel.accept();
+ QCoreApplication::sendEvent(item, &wheel);
+ if (wheel.isAccepted()) {
+ qCDebug(lcWheelTarget) << &wheel << "->" << item;
+ event->setAccepted(true);
+ return true;
+ }
+ }
+#endif
+#if QT_CONFIG(gestures)
+ if (QQuickPointerNativeGestureEvent *pnge = event->asPointerNativeGestureEvent()) {
+ QNativeGestureEvent nge(pnge->type(), pnge->device()->qTouchDevice(), point->position(), point->scenePosition(), g,
+ pnge->value(), 0L, 0L); // TODO can't copy things I can't access
+ nge.accept();
+ QCoreApplication::sendEvent(item, &nge);
+ if (nge.isAccepted()) {
+ qCDebug(lcGestureTarget) << &nge << "->" << item;
+ event->setAccepted(true);
+ return true;
+ }
}
+#endif // gestures
}
- return false;
+ return false; // it wasn't handled
}
+#if QT_CONFIG(wheelevent)
/*! \reimp */
void QQuickWindow::wheelEvent(QWheelEvent *event)
{
@@ -1937,55 +1987,11 @@ void QQuickWindow::wheelEvent(QWheelEvent *event)
return;
event->ignore();
- d->deliverWheelEvent(d->contentItem, event);
+ d->deliverPointerEvent(d->pointerEventInstance(event));
d->lastWheelEventAccepted = event->isAccepted();
}
#endif // wheelevent
-#if QT_CONFIG(gestures)
-bool QQuickWindowPrivate::deliverNativeGestureEvent(QQuickItem *item, QNativeGestureEvent *event)
-{
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
-
- QPointF p = item->mapFromScene(event->windowPos());
- if ((itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) && !item->contains(p))
- return false;
-
- QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
- for (int ii = children.count() - 1; ii >= 0; --ii) {
- QQuickItem *child = children.at(ii);
- if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
- continue;
- if (deliverNativeGestureEvent(child, event))
- return true;
- }
-
- // Try the Item's pointer handlers first
- QQuickPointerEvent *pointerEvent = pointerEventInstance(event);
- pointerEvent->localize(item);
- if (itemPrivate->handlePointerEvent(pointerEvent, false)) {
- if (pointerEvent->allPointsAccepted()) {
- event->accept();
- return true;
- }
- }
-
- // If still not accepted, try direct delivery to the item
- if (item->contains(p)) {
- QNativeGestureEvent copy(event->gestureType(), event->device(), p, event->windowPos(), event->screenPos(),
- event->value(), 0L, 0L); // TODO can't copy things I can't access
- event->accept();
- item->event(&copy);
- if (copy.isAccepted()) {
- event->accept();
- return true;
- }
- }
-
- return false;
-}
-#endif // gestures
-
bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
{
qCDebug(DBG_TOUCH) << event;
@@ -2229,6 +2235,8 @@ QQuickPointerEvent *QQuickWindowPrivate::queryPointerEventInstance(QQuickPointer
if (eventType == QEvent::NativeGesture && !qobject_cast<QQuickPointerNativeGestureEvent*>(e))
continue;
#endif
+ if (eventType == QEvent::Wheel && !qobject_cast<QQuickPointerScrollEvent*>(e))
+ continue;
// Otherwise we assume there's only one event type per device.
// More disambiguation tests might need to be added above if that changes later.
if (e->device() == device)
@@ -2248,7 +2256,10 @@ QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevic
// QWindowSystemInterface::handleMouseEvent() does not take a device parameter:
// we assume all mouse events come from one mouse (the "core pointer").
// So when the event is a mouse event, device == QQuickPointerDevice::genericMouseDevice()
- ev = new QQuickPointerMouseEvent(q, device);
+ if (eventType == QEvent::Wheel)
+ ev = new QQuickPointerScrollEvent(q, device);
+ else
+ ev = new QQuickPointerMouseEvent(q, device);
break;
case QQuickPointerDevice::TouchPad:
case QQuickPointerDevice::TouchScreen:
@@ -2282,6 +2293,7 @@ QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event) con
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
+ case QEvent::Wheel:
dev = QQuickPointerDevice::genericMouseDevice();
break;
case QEvent::TouchBegin:
@@ -2324,7 +2336,7 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event)
} else if (event->asPointerTouchEvent()) {
deliverTouchEvent(event->asPointerTouchEvent());
} else {
- Q_ASSERT(false);
+ deliverSinglePointEventUntilAccepted(event);
}
event->reset(nullptr);
@@ -2332,16 +2344,16 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event)
--pointerEventRecursionGuard;
}
-// check if item or any of its child items contain the point
+// check if item or any of its child items contain the point, or if any pointer handler "wants" the point
// FIXME: should this be iterative instead of recursive?
// If checkMouseButtons is true, it means we are finding targets for a mouse event, so no item for which acceptedMouseButtons() is NoButton will be added.
// If checkAcceptsTouch is true, it means we are finding targets for a touch event, so either acceptTouchEvents() must return true OR
// it must accept a synth. mouse event, thus if acceptTouchEvents() returns false but acceptedMouseButtons() is true, gets added; if not, it doesn't.
-QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos, bool checkMouseButtons, bool checkAcceptsTouch) const
+QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, QQuickEventPoint *point, bool checkMouseButtons, bool checkAcceptsTouch) const
{
QVector<QQuickItem *> targets;
auto itemPrivate = QQuickItemPrivate::get(item);
- QPointF itemPos = item->mapFromScene(scenePos);
+ QPointF itemPos = item->mapFromScene(point->scenePosition());
// if the item clips, we can potentially return early
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
if (!item->contains(itemPos))
@@ -2355,11 +2367,15 @@ QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, cons
auto childPrivate = QQuickItemPrivate::get(child);
if (!child->isVisible() || !child->isEnabled() || childPrivate->culled)
continue;
- targets << pointerTargets(child, scenePos, checkMouseButtons, checkAcceptsTouch);
+ targets << pointerTargets(child, point, checkMouseButtons, checkAcceptsTouch);
}
bool relevant = item->contains(itemPos);
- if (!(itemPrivate->hasPointerHandlers())) {
+ if (itemPrivate->hasPointerHandlers()) {
+ if (!relevant)
+ if (itemPrivate->anyPointerHandlerWants(point))
+ relevant = true;
+ } else {
if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton)
relevant = false;
if (relevant && checkAcceptsTouch && !(item->acceptTouchEvents() || item->acceptedMouseButtons()))
@@ -2440,6 +2456,7 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve
// The grabber is not an item? It's a handler then. Let it have the event first.
QQuickPointerHandler *handler = static_cast<QQuickPointerHandler *>(grabber);
receiver = static_cast<QQuickPointerHandler *>(grabber)->parentItem();
+ hasFiltered.clear();
if (sendFilteredPointerEvent(event, receiver))
done = true;
event->localize(receiver);
@@ -2469,7 +2486,7 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve
QQuickEventPoint *point = event->point(i);
if (point->state() == QQuickEventPoint::Pressed)
continue; // presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints
- QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePosition(), false, false);
+ QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, false, false);
if (targetItems.count()) {
targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
} else {
@@ -2499,7 +2516,7 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event,
point->setAccepted(false); // because otherwise touchEventForItem will ignore it
if (point->grabberPointerHandler() && point->state() == QQuickEventPoint::Released)
point->setGrabberPointerHandler(nullptr, true);
- QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePosition(), !isTouchEvent, isTouchEvent);
+ QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, !isTouchEvent, isTouchEvent);
if (targetItems.count()) {
targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
} else {
@@ -2508,6 +2525,7 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event,
}
for (QQuickItem *item : targetItems) {
+ hasFiltered.clear();
if (!handlersOnly && sendFilteredPointerEvent(event, item)) {
if (event->isAccepted()) {
for (int i = 0; i < event->pointCount(); ++i)
@@ -2559,7 +2577,6 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
// synthetic events - flickable sends one when setPressDelay is used.
auto oldMouseGrabber = q->mouseGrabberItem();
QPointF localPos = item->mapFromScene(point->scenePosition());
- Q_ASSERT(item->contains(localPos)); // transform is checked already
QMouseEvent *me = event->asMouseEvent(localPos);
me->accept();
QCoreApplication::sendEvent(item, me);
@@ -2587,6 +2604,7 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
bool eventAccepted = false;
// If any parent filters the event, we're done.
+ hasFiltered.clear();
if (sendFilteredPointerEvent(pointerEvent, item))
return;
@@ -2800,7 +2818,6 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF
bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
{
- hasFiltered.clear();
return sendFilteredPointerEventImpl(event, receiver, filteringParent ? filteringParent : receiver->parentItem());
}
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index b5e3a2c1eb..9a83e8523b 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -152,12 +152,7 @@ public:
bool sendFilteredMouseEvent(QEvent *event, QQuickItem *receiver, QQuickItem *filteringParent);
bool sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent = nullptr);
bool sendFilteredPointerEventImpl(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent);
-#if QT_CONFIG(wheelevent)
- bool deliverWheelEvent(QQuickItem *, QWheelEvent *);
-#endif
-#if QT_CONFIG(gestures)
- bool deliverNativeGestureEvent(QQuickItem *, QNativeGestureEvent *);
-#endif
+ bool deliverSinglePointEventUntilAccepted(QQuickPointerEvent *);
// entry point of events to the window
void handleTouchEvent(QTouchEvent *);
@@ -180,7 +175,7 @@ public:
void deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event);
void deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly = false);
- QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons, bool checkAcceptsTouch) const;
+ QVector<QQuickItem *> pointerTargets(QQuickItem *, QQuickEventPoint *point, bool checkMouseButtons, bool checkAcceptsTouch) const;
QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const;
// hover delivery
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
index d715d900ba..2e5fdbbe6b 100644
--- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
@@ -77,6 +77,11 @@ QSGSoftwareRenderableNode *QSGAbstractSoftwareRenderer::renderableNode(QSGNode *
return m_nodes.value(node, nullptr);
}
+const QLinkedList<QSGSoftwareRenderableNode*> &QSGAbstractSoftwareRenderer::renderableNodes() const
+{
+ return m_renderableNodes;
+}
+
void QSGAbstractSoftwareRenderer::addNodeMapping(QSGNode *node, QSGSoftwareRenderableNode *renderableNode)
{
m_nodes.insert(node, renderableNode);
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
index f6594d931a..99204ef25e 100644
--- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
@@ -88,6 +88,7 @@ protected:
QRect backgroundRect();
// only known after calling optimizeRenderList()
bool isOpaque() const { return m_isOpaque; }
+ const QLinkedList<QSGSoftwareRenderableNode*> &renderableNodes() const;
private:
void nodeAdded(QSGNode *node);
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp
index 21f20c66cd..7ab9c15d9b 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp
@@ -91,8 +91,8 @@ void QSGSoftwareGlyphNode::paint(QPainter *painter)
QPointF pos = m_position - QPointF(0, m_glyphRun.rawFont().ascent());
qreal offset = 1.0;
- if (painter->device()->devicePixelRatio() != 0)
- offset = 1.0 / painter->device()->devicePixelRatio();
+ if (painter->device()->devicePixelRatioF() > 0.0)
+ offset = 1.0 / painter->device()->devicePixelRatioF();
switch (m_style) {
case QQuickText::Normal: break;
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
index bf3141bc32..2c361e03e2 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
@@ -47,6 +47,7 @@ QT_BEGIN_NAMESPACE
QSGSoftwareInternalRectangleNode::QSGSoftwareInternalRectangleNode()
: m_penWidth(0)
, m_radius(0)
+ , m_vertical(true)
, m_cornerPixmapIsDirty(true)
, m_devicePixelRatio(1)
{
@@ -186,6 +187,15 @@ void QSGSoftwareInternalRectangleNode::setGradientStops(const QGradientStops &st
markDirty(DirtyMaterial);
}
+void QSGSoftwareInternalRectangleNode::setGradientVertical(bool vertical)
+{
+ if (m_vertical != vertical) {
+ m_vertical = vertical;
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
void QSGSoftwareInternalRectangleNode::setRadius(qreal radius)
{
if (m_radius != radius) {
@@ -209,7 +219,7 @@ void QSGSoftwareInternalRectangleNode::update()
}
if (!m_stops.isEmpty()) {
- QLinearGradient gradient(QPoint(0,0), QPoint(0,1));
+ QLinearGradient gradient(QPoint(0,0), QPoint(m_vertical ? 0 : 1, m_vertical ? 1 : 0));
gradient.setStops(m_stops);
gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
m_brush = QBrush(gradient);
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h
index 1f87424d2a..125520de26 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h
@@ -69,6 +69,7 @@ public:
void setPenColor(const QColor &color) override;
void setPenWidth(qreal width) override;
void setGradientStops(const QGradientStops &stops) override;
+ void setGradientVertical(bool vertical) override;
void setRadius(qreal radius) override;
void setAntialiasing(bool antialiasing) override { Q_UNUSED(antialiasing) }
void setAligned(bool aligned) override;
@@ -91,6 +92,7 @@ private:
double m_radius;
QPen m_pen;
QBrush m_brush;
+ bool m_vertical;
bool m_cornerPixmapIsDirty;
QPixmap m_cornerPixmap;
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
index 8fc87db179..b20d0a1828 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
@@ -72,7 +72,7 @@ class QSGSoftwareNinePatchNode;
class QSGSoftwareSpriteNode;
class QSGRenderNode;
-class QSGSoftwareRenderableNode
+class Q_QUICK_PRIVATE_EXPORT QSGSoftwareRenderableNode
{
public:
enum NodeType {
@@ -104,6 +104,7 @@ public:
bool isOpaque() const { return m_isOpaque; }
bool isDirty() const { return m_isDirty; }
bool isDirtyRegionEmpty() const;
+ QSGNode *handle() const { return m_handle.node; }
void setTransform(const QTransform &transform);
void setClipRegion(const QRegion &clipRegion, bool hasClipRegion = true);
@@ -123,6 +124,7 @@ public:
private:
union RenderableNodeHandle {
+ QSGNode *node;
QSGSimpleRectNode *simpleRectNode;
QSGSimpleTextureNode *simpleTextureNode;
QSGSoftwareInternalImageNode *imageNode;
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
index ffcee5f56e..e9ed52d428 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
@@ -113,8 +113,8 @@ void QSGSoftwareRenderer::render()
setBackgroundColor(clearColor());
setBackgroundRect(QRect(0, 0,
- m_paintDevice->width() / m_paintDevice->devicePixelRatio(),
- m_paintDevice->height() / m_paintDevice->devicePixelRatio()));
+ m_paintDevice->width() / m_paintDevice->devicePixelRatioF(),
+ m_paintDevice->height() / m_paintDevice->devicePixelRatioF()));
// Build Renderlist
// The renderlist is created by visiting each node in the tree and when a
diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp
index c64360f955..46af0f28f0 100644
--- a/src/quick/scenegraph/qsgadaptationlayer.cpp
+++ b/src/quick/scenegraph/qsgadaptationlayer.cpp
@@ -234,10 +234,8 @@ void QSGDistanceFieldGlyphCache::setGlyphsPosition(const QList<GlyphPosition> &g
}
if (!invalidatedGlyphs.isEmpty()) {
- QLinkedList<QSGDistanceFieldGlyphConsumer *>::iterator it = m_registeredNodes.begin();
- while (it != m_registeredNodes.end()) {
- (*it)->invalidateGlyphs(invalidatedGlyphs);
- ++it;
+ for (QSGDistanceFieldGlyphConsumerList::iterator iter = m_registeredNodes.begin(); iter != m_registeredNodes.end(); ++iter) {
+ iter->invalidateGlyphs(invalidatedGlyphs);
}
}
}
@@ -280,10 +278,8 @@ void QSGDistanceFieldGlyphCache::setGlyphsTexture(const QVector<glyph_t> &glyphs
}
if (!invalidatedGlyphs.isEmpty()) {
- QLinkedList<QSGDistanceFieldGlyphConsumer*>::iterator it = m_registeredNodes.begin();
- while (it != m_registeredNodes.end()) {
- (*it)->invalidateGlyphs(invalidatedGlyphs);
- ++it;
+ for (QSGDistanceFieldGlyphConsumerList::iterator iter = m_registeredNodes.begin(); iter != m_registeredNodes.end(); ++iter) {
+ iter->invalidateGlyphs(invalidatedGlyphs);
}
}
}
diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h
index 9c88b8272c..5a465831cb 100644
--- a/src/quick/scenegraph/qsgadaptationlayer_p.h
+++ b/src/quick/scenegraph/qsgadaptationlayer_p.h
@@ -64,6 +64,7 @@
#include <QtGui/private/qdatabuffer_p.h>
#include <private/qopenglcontext_p.h>
#include <private/qdistancefield_p.h>
+#include <private/qintrusivelist_p.h>
// ### remove
#include <QtQuick/private/qquicktext_p.h>
@@ -136,6 +137,7 @@ public:
virtual void setPenColor(const QColor &color) = 0;
virtual void setPenWidth(qreal width) = 0;
virtual void setGradientStops(const QGradientStops &stops) = 0;
+ virtual void setGradientVertical(bool vertical) = 0;
virtual void setRadius(qreal radius) = 0;
virtual void setAntialiasing(bool antialiasing) { Q_UNUSED(antialiasing) }
virtual void setAligned(bool aligned) = 0;
@@ -403,7 +405,9 @@ public:
virtual ~QSGDistanceFieldGlyphConsumer() {}
virtual void invalidateGlyphs(const QVector<quint32> &glyphs) = 0;
+ QIntrusiveListNode node;
};
+typedef QIntrusiveList<QSGDistanceFieldGlyphConsumer, &QSGDistanceFieldGlyphConsumer::node> QSGDistanceFieldGlyphConsumerList;
class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldGlyphCache
{
@@ -464,8 +468,8 @@ public:
void update();
- void registerGlyphNode(QSGDistanceFieldGlyphConsumer *node) { m_registeredNodes.append(node); }
- void unregisterGlyphNode(QSGDistanceFieldGlyphConsumer *node) { m_registeredNodes.removeOne(node); }
+ void registerGlyphNode(QSGDistanceFieldGlyphConsumer *node) { m_registeredNodes.insert(node); }
+ void unregisterGlyphNode(QSGDistanceFieldGlyphConsumer *node) { m_registeredNodes.remove(node); }
virtual void registerOwnerElement(QQuickItem *ownerElement);
virtual void unregisterOwnerElement(QQuickItem *ownerElement);
@@ -521,7 +525,7 @@ private:
QHash<glyph_t, GlyphData> m_glyphsData;
QDataBuffer<glyph_t> m_pendingGlyphs;
QSet<glyph_t> m_populatingGlyphs;
- QLinkedList<QSGDistanceFieldGlyphConsumer*> m_registeredNodes;
+ QSGDistanceFieldGlyphConsumerList m_registeredNodes;
static Texture s_emptyTexture;
};
diff --git a/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp b/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp
index df124cce35..91317ee2d7 100644
--- a/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp
+++ b/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp
@@ -68,19 +68,30 @@ namespace
{
float x, y;
Color4ub color;
- void set(float nx, float ny, Color4ub ncolor)
+
+ void set(float primary, float secondary, Color4ub ncolor, bool vertical)
{
- x = nx; y = ny; color = ncolor;
+ if (vertical) {
+ x = secondary; y = primary;
+ } else {
+ x = primary; y = secondary;
+ }
+ color = ncolor;
}
};
struct SmoothVertex : public Vertex
{
float dx, dy;
- void set(float nx, float ny, Color4ub ncolor, float ndx, float ndy)
+
+ void set(float primary, float secondary, Color4ub ncolor, float dPrimary, float dSecondary, bool vertical)
{
- Vertex::set(nx, ny, ncolor);
- dx = ndx; dy = ndy;
+ Vertex::set(primary, secondary, ncolor, vertical);
+ if (vertical) {
+ dx = dSecondary; dy = dPrimary;
+ } else {
+ dx = dPrimary; dy = dSecondary;
+ }
}
};
@@ -103,6 +114,7 @@ QSGBasicInternalRectangleNode::QSGBasicInternalRectangleNode()
, m_antialiasing(false)
, m_gradient_is_opaque(true)
, m_dirty_geometry(false)
+ , m_gradient_is_vertical(true)
, m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0)
{
setGeometry(&m_geometry);
@@ -160,6 +172,15 @@ void QSGBasicInternalRectangleNode::setGradientStops(const QGradientStops &stops
m_dirty_geometry = true;
}
+void QSGBasicInternalRectangleNode::setGradientVertical(bool vertical)
+{
+ if (vertical == m_gradient_is_vertical)
+ return;
+ m_gradient_is_vertical = vertical;
+ m_dirty_geometry = true;
+}
+
+
void QSGBasicInternalRectangleNode::setRadius(qreal radius)
{
if (radius == m_radius)
@@ -230,12 +251,15 @@ void QSGBasicInternalRectangleNode::updateGeometry()
Color4ub transparent = { 0, 0, 0, 0 };
const QGradientStops &stops = m_gradient_stops;
+ float length = (m_gradient_is_vertical ? height : width);
+ float secondaryLength = (m_gradient_is_vertical ? width : height);
+
int nextGradientStop = 0;
- float gradientPos = penWidth / height;
+ float gradientPos = penWidth / length;
while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos)
++nextGradientStop;
int lastGradientStop = stops.size() - 1;
- float lastGradientPos = 1.0f - penWidth / height;
+ float lastGradientPos = 1.0f - penWidth / length;
while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos)
--lastGradientStop;
int gradientIntersections = (lastGradientStop - nextGradientStop + 1);
@@ -319,40 +343,46 @@ void QSGBasicInternalRectangleNode::updateGeometry()
quint16 *indices = g->indexDataAsUShort();
quint16 index = 0;
- float py = 0; // previous inner y-coordinate.
- float plx = 0; // previous inner left x-coordinate.
- float prx = 0; // previous inner right x-coordinate.
+ float pp = 0; // previous inner primary coordinate.
+ float pss = 0; // previous inner secondary start coordinate.
+ float pse = 0; // previous inner secondary end coordinate.
float angle = 0.5f * float(M_PI) / segments;
float cosStep = qFastCos(angle);
float sinStep = qFastSin(angle);
+ float innerStart = (m_gradient_is_vertical ? innerRect.top() : innerRect.left());
+ float innerEnd = (m_gradient_is_vertical ? innerRect.bottom() : innerRect.right());
+ float innerLength = (m_gradient_is_vertical ? innerRect.height() : innerRect.width());
+ float innerSecondaryStart = (m_gradient_is_vertical ? innerRect.left() : innerRect.top());
+ float innerSecondaryEnd = (m_gradient_is_vertical ? innerRect.right() : innerRect.bottom());
+
for (int part = 0; part < 2; ++part) {
float c = 1 - part;
float s = part;
for (int i = 0; i <= segments; ++i) {
- float y, lx, rx;
+ float p, ss, se;
if (innerRadius > 0) {
- y = (part ? innerRect.bottom() : innerRect.top()) - innerRadius * c; // current inner y-coordinate.
- lx = innerRect.left() - innerRadius * s; // current inner left x-coordinate.
- rx = innerRect.right() + innerRadius * s; // current inner right x-coordinate.
- gradientPos = ((part ? innerRect.height() : 0) + radius - innerRadius * c) / height;
+ p = (part ? innerEnd : innerStart) - innerRadius * c; // current inner primary coordinate.
+ ss = innerSecondaryStart - innerRadius * s; // current inner secondary start coordinate.
+ se = innerSecondaryEnd + innerRadius * s; // current inner secondary end coordinate.
+ gradientPos = ((part ? innerLength : 0) + radius - innerRadius * c) / length;
} else {
- y = (part ? innerRect.bottom() + innerRadius : innerRect.top() - innerRadius); // current inner y-coordinate.
- lx = innerRect.left() - innerRadius; // current inner left x-coordinate.
- rx = innerRect.right() + innerRadius; // current inner right x-coordinate.
- gradientPos = ((part ? innerRect.height() + innerRadius : -innerRadius) + radius) / height;
+ p = (part ? innerEnd + innerRadius : innerStart - innerRadius); // current inner primary coordinate.
+ ss = innerSecondaryStart - innerRadius; // current inner secondary start coordinate.
+ se = innerSecondaryEnd + innerRadius; // current inner secondary end coordinate.
+ gradientPos = ((part ? innerLength + innerRadius : -innerRadius) + radius) / length;
}
- float Y = (part ? innerRect.bottom() : innerRect.top()) - outerRadius * c; // current outer y-coordinate.
- float lX = innerRect.left() - outerRadius * s; // current outer left x-coordinate.
- float rX = innerRect.right() + outerRadius * s; // current outer right x-coordinate.
+ float outerEdge = (part ? innerEnd : innerStart) - outerRadius * c; // current outer primary coordinate.
+ float outerSecondaryStart = innerSecondaryStart - outerRadius * s; // current outer secondary start coordinate.
+ float outerSecondaryEnd = innerSecondaryEnd + outerRadius * s; // current outer secondary end coordinate.
while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) {
// Insert vertices at gradient stops.
- float gy = (innerRect.top() - radius) + stops.at(nextGradientStop).first * height;
- float t = (gy - py) / (y - py);
- float glx = plx * (1 - t) + t * lx;
- float grx = prx * (1 - t) + t * rx;
+ float gp = (innerStart - radius) + stops.at(nextGradientStop).first * length;
+ float t = (gp - pp) / (p - pp);
+ float gis = pss * (1 - t) + t * ss; // gradient inner start
+ float gie = pse * (1 - t) + t * se; // gradient inner end
fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
@@ -377,23 +407,23 @@ void QSGBasicInternalRectangleNode::updateGeometry()
indices[innerAATail++] = index + 3;
bool lower = stops.at(nextGradientStop).first > 0.5f;
- float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy);
- smoothVertices[index++].set(grx, gy, fillColor, width - grx - delta, dy);
- smoothVertices[index++].set(glx, gy, fillColor, delta - glx, dy);
+ float dp = lower ? qMin(0.0f, length - gp - delta) : qMax(0.0f, delta - gp);
+ smoothVertices[index++].set(gp, gie, fillColor, dp, secondaryLength - gie - delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(gp, gis, fillColor, dp, delta - gis, m_gradient_is_vertical);
if (penWidth) {
- smoothVertices[index++].set(grx, gy, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c);
- smoothVertices[index++].set(glx, gy, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c);
+ smoothVertices[index++].set(gp, gie, borderColor, -0.49f * penWidth * c, 0.49f * penWidth * s, m_gradient_is_vertical);
+ smoothVertices[index++].set(gp, gis, borderColor, -0.49f * penWidth * c, -0.49f * penWidth * s, m_gradient_is_vertical);
} else {
- dy = lower ? delta : -delta;
- smoothVertices[index++].set(grx, gy, transparent, delta, dy);
- smoothVertices[index++].set(glx, gy, transparent, -delta, dy);
+ dp = lower ? delta : -delta;
+ smoothVertices[index++].set(gp, gie, transparent, dp, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(gp, gis, transparent, dp, -delta, m_gradient_is_vertical);
}
} else {
- vertices[index++].set(grx, gy, fillColor);
- vertices[index++].set(glx, gy, fillColor);
+ vertices[index++].set(gp, gie, fillColor, m_gradient_is_vertical);
+ vertices[index++].set(gp, gis, fillColor, m_gradient_is_vertical);
if (penWidth) {
- vertices[index++].set(grx, gy, borderColor);
- vertices[index++].set(glx, gy, borderColor);
+ vertices[index++].set(gp, gie, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(gp, gis, borderColor, m_gradient_is_vertical);
}
}
++nextGradientStop;
@@ -430,41 +460,41 @@ void QSGBasicInternalRectangleNode::updateGeometry()
indices[innerAATail++] = index + 1;
indices[innerAATail++] = index + 3;
- float dy = part ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y);
- smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy);
- smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy);
+ float dp = part ? qMin(0.0f, length - p - delta) : qMax(0.0f, delta - p);
+ smoothVertices[index++].set(p, se, fillColor, dp, secondaryLength - se - delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(p, ss, fillColor, dp, delta - ss, m_gradient_is_vertical);
- dy = part ? delta : -delta;
+ dp = part ? delta : -delta;
if (penWidth) {
- smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c);
- smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c);
- smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth * s, 0.49f * penWidth * c);
- smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth * s, 0.49f * penWidth * c);
- smoothVertices[index++].set(rX, Y, transparent, delta, dy);
- smoothVertices[index++].set(lX, Y, transparent, -delta, dy);
+ smoothVertices[index++].set(p, se, borderColor, -0.49f * penWidth * c, 0.49f * penWidth * s, m_gradient_is_vertical);
+ smoothVertices[index++].set(p, ss, borderColor, -0.49f * penWidth * c, -0.49f * penWidth * s, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, 0.49f * penWidth * c, -0.49f * penWidth * s, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerEdge, outerSecondaryStart, borderColor, 0.49f * penWidth * c, 0.49f * penWidth * s, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerEdge, outerSecondaryEnd, transparent, dp, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerEdge, outerSecondaryStart, transparent, dp, -delta, m_gradient_is_vertical);
indices[--outerAAHead] = index - 2;
indices[--outerAAHead] = index - 4;
indices[outerAATail++] = index - 3;
indices[outerAATail++] = index - 1;
} else {
- smoothVertices[index++].set(rx, y, transparent, delta, dy);
- smoothVertices[index++].set(lx, y, transparent, -delta, dy);
+ smoothVertices[index++].set(p, se, transparent, dp, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(p, ss, transparent, dp, -delta, m_gradient_is_vertical);
}
} else {
- vertices[index++].set(rx, y, fillColor);
- vertices[index++].set(lx, y, fillColor);
+ vertices[index++].set(p, se, fillColor, m_gradient_is_vertical);
+ vertices[index++].set(p, ss, fillColor, m_gradient_is_vertical);
if (penWidth) {
- vertices[index++].set(rx, y, borderColor);
- vertices[index++].set(lx, y, borderColor);
- vertices[index++].set(rX, Y, borderColor);
- vertices[index++].set(lX, Y, borderColor);
+ vertices[index++].set(p, se, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(p, ss, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(outerEdge, outerSecondaryStart, borderColor, m_gradient_is_vertical);
}
}
- py = y;
- plx = lx;
- prx = rx;
+ pp = p;
+ pss = ss;
+ pse = se;
// Rotate
qreal tmp = c;
@@ -543,19 +573,24 @@ void QSGBasicInternalRectangleNode::updateGeometry()
quint16 *indices = g->indexDataAsUShort();
quint16 index = 0;
- float lx = innerRect.left();
- float rx = innerRect.right();
- float lX = outerRect.left();
- float rX = outerRect.right();
+ float innerStart = (m_gradient_is_vertical ? innerRect.top() : innerRect.left());
+ float innerEnd = (m_gradient_is_vertical ? innerRect.bottom() : innerRect.right());
+ float outerStart = (m_gradient_is_vertical ? outerRect.top() : outerRect.left());
+ float outerEnd = (m_gradient_is_vertical ? outerRect.bottom() : outerRect.right());
+
+ float innerSecondaryStart = (m_gradient_is_vertical ? innerRect.left() : innerRect.top());
+ float innerSecondaryEnd = (m_gradient_is_vertical ? innerRect.right() : innerRect.bottom());
+ float outerSecondaryStart = (m_gradient_is_vertical ? outerRect.left() : outerRect.top());
+ float outerSecondaryEnd = (m_gradient_is_vertical ? outerRect.right() : outerRect.bottom());
for (int part = -1; part <= 1; part += 2) {
- float y = (part == 1 ? innerRect.bottom() : innerRect.top());
- float Y = (part == 1 ? outerRect.bottom() : outerRect.top());
- gradientPos = (y - innerRect.top() + penWidth) / height;
+ float innerEdge = (part == 1 ? innerEnd : innerStart);
+ float outerEdge = (part == 1 ? outerEnd : outerStart);
+ gradientPos = (innerEdge - innerStart + penWidth) / length;
while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) {
// Insert vertices at gradient stops.
- float gy = (innerRect.top() - penWidth) + stops.at(nextGradientStop).first * height;
+ float gp = (innerStart - penWidth) + stops.at(nextGradientStop).first * length;
fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
@@ -580,22 +615,22 @@ void QSGBasicInternalRectangleNode::updateGeometry()
indices[innerAATail++] = index + 3;
bool lower = stops.at(nextGradientStop).first > 0.5f;
- float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy);
- smoothVertices[index++].set(rx, gy, fillColor, width - rx - delta, dy);
- smoothVertices[index++].set(lx, gy, fillColor, delta - lx, dy);
+ float dp = lower ? qMin(0.0f, length - gp - delta) : qMax(0.0f, delta - gp);
+ smoothVertices[index++].set(gp, innerSecondaryEnd, fillColor, dp, secondaryLength - innerSecondaryEnd - delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(gp, innerSecondaryStart, fillColor, dp, delta - innerSecondaryStart, m_gradient_is_vertical);
if (penWidth) {
- smoothVertices[index++].set(rx, gy, borderColor, 0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth);
- smoothVertices[index++].set(lx, gy, borderColor, -0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth);
+ smoothVertices[index++].set(gp, innerSecondaryEnd, borderColor, (lower ? 0.49f : -0.49f) * penWidth, 0.49f * penWidth, m_gradient_is_vertical);
+ smoothVertices[index++].set(gp, innerSecondaryStart, borderColor, (lower ? 0.49f : -0.49f) * penWidth, -0.49f * penWidth, m_gradient_is_vertical);
} else {
- smoothVertices[index++].set(rx, gy, transparent, delta, lower ? delta : -delta);
- smoothVertices[index++].set(lx, gy, transparent, -delta, lower ? delta : -delta);
+ smoothVertices[index++].set(gp, innerSecondaryEnd, transparent, lower ? delta : -delta, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(gp, innerSecondaryStart, transparent, lower ? delta : -delta, -delta, m_gradient_is_vertical);
}
} else {
- vertices[index++].set(rx, gy, fillColor);
- vertices[index++].set(lx, gy, fillColor);
+ vertices[index++].set(gp, innerSecondaryEnd, fillColor, m_gradient_is_vertical);
+ vertices[index++].set(gp, innerSecondaryStart, fillColor, m_gradient_is_vertical);
if (penWidth) {
- vertices[index++].set(rx, gy, borderColor);
- vertices[index++].set(lx, gy, borderColor);
+ vertices[index++].set(gp, innerSecondaryEnd, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(gp, innerSecondaryStart, borderColor, m_gradient_is_vertical);
}
}
++nextGradientStop;
@@ -632,34 +667,34 @@ void QSGBasicInternalRectangleNode::updateGeometry()
indices[innerAATail++] = index + 1;
indices[innerAATail++] = index + 3;
- float dy = part == 1 ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y);
- smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy);
- smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy);
+ float dp = part == 1 ? qMin(0.0f, length - innerEdge - delta) : qMax(0.0f, delta - innerEdge);
+ smoothVertices[index++].set(innerEdge, innerSecondaryEnd, fillColor, dp, secondaryLength - innerSecondaryEnd - delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(innerEdge, innerSecondaryStart, fillColor, dp, delta - innerSecondaryStart, m_gradient_is_vertical);
if (penWidth) {
- smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth, 0.49f * penWidth * part);
- smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth, 0.49f * penWidth * part);
- smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth, -0.49f * penWidth * part);
- smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth, -0.49f * penWidth * part);
- smoothVertices[index++].set(rX, Y, transparent, delta, delta * part);
- smoothVertices[index++].set(lX, Y, transparent, -delta, delta * part);
+ smoothVertices[index++].set(innerEdge, innerSecondaryEnd, borderColor, 0.49f * penWidth * part, 0.49f * penWidth, m_gradient_is_vertical);
+ smoothVertices[index++].set(innerEdge, innerSecondaryStart, borderColor, 0.49f * penWidth * part, -0.49f * penWidth, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, -0.49f * penWidth * part, -0.49f * penWidth, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerEdge, outerSecondaryStart, borderColor, -0.49f * penWidth * part, 0.49f * penWidth, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerEdge, outerSecondaryEnd, transparent, delta * part, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerEdge, outerSecondaryStart, transparent, delta * part, -delta, m_gradient_is_vertical);
indices[--outerAAHead] = index - 2;
indices[--outerAAHead] = index - 4;
indices[outerAATail++] = index - 3;
indices[outerAATail++] = index - 1;
} else {
- smoothVertices[index++].set(rx, y, transparent, delta, delta * part);
- smoothVertices[index++].set(lx, y, transparent, -delta, delta * part);
+ smoothVertices[index++].set(innerEdge, innerSecondaryEnd, transparent, delta * part, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(innerEdge, innerSecondaryStart, transparent, delta * part, -delta, m_gradient_is_vertical);
}
} else {
- vertices[index++].set(rx, y, fillColor);
- vertices[index++].set(lx, y, fillColor);
+ vertices[index++].set(innerEdge, innerSecondaryEnd, fillColor, m_gradient_is_vertical);
+ vertices[index++].set(innerEdge, innerSecondaryStart, fillColor, m_gradient_is_vertical);
if (penWidth) {
- vertices[index++].set(rx, y, borderColor);
- vertices[index++].set(lx, y, borderColor);
- vertices[index++].set(rX, Y, borderColor);
- vertices[index++].set(lX, Y, borderColor);
+ vertices[index++].set(innerEdge, innerSecondaryEnd, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(innerEdge, innerSecondaryStart, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(outerEdge, outerSecondaryStart, borderColor, m_gradient_is_vertical);
}
}
}
diff --git a/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h b/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h
index 98e53669ce..99f26b9aed 100644
--- a/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h
+++ b/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h
@@ -66,6 +66,7 @@ public:
void setPenColor(const QColor &color) override;
void setPenWidth(qreal width) override;
void setGradientStops(const QGradientStops &stops) override;
+ void setGradientVertical(bool vertical) override;
void setRadius(qreal radius) override;
void setAntialiasing(bool antialiasing) override;
void setAligned(bool aligned) override;
@@ -90,6 +91,7 @@ protected:
uint m_antialiasing : 1;
uint m_gradient_is_opaque : 1;
uint m_dirty_geometry : 1;
+ uint m_gradient_is_vertical : 1;
QSGGeometry m_geometry;
};
diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp
index 9ee1a323e5..02be9daac0 100644
--- a/src/quick/util/qquickanimation.cpp
+++ b/src/quick/util/qquickanimation.cpp
@@ -234,6 +234,27 @@ QQmlProperty QQuickAbstractAnimationPrivate::createProperty(QObject *obj, const
The corresponding handler is \c onStopped.
*/
+/*!
+ \qmlsignal QtQuick::Animation::finished()
+ \since 5.12
+
+ This signal is emitted when the animation has finished naturally.
+
+ It is not emitted when \l running is set to \c false, nor for animations whose
+ \l loops property is set to \c Animation.Infinite.
+
+ In addition, it is only emitted for top-level, standalone animations. It
+ will not be emitted for animations in a Behavior or Transition, or
+ animations that are part of an animation group.
+
+ If \l alwaysRunToEnd is true, this signal will not be emitted until the
+ animation has completed its current iteration.
+
+ The corresponding handler is \c onFinished.
+
+ \sa stopped(), started(), running
+*/
+
void QQuickAbstractAnimation::setRunning(bool r)
{
Q_D(QQuickAbstractAnimation);
@@ -656,6 +677,7 @@ void QQuickAbstractAnimationPrivate::animationFinished(QAbstractAnimationJob*)
if (loopCount != 1)
animationInstance->setLoopCount(loopCount);
}
+ emit q->finished();
}
QQuickAbstractAnimation::ThreadingModel QQuickAbstractAnimation::threadingModel() const
diff --git a/src/quick/util/qquickanimation_p.h b/src/quick/util/qquickanimation_p.h
index 2293f2597f..746cb938bd 100644
--- a/src/quick/util/qquickanimation_p.h
+++ b/src/quick/util/qquickanimation_p.h
@@ -126,6 +126,7 @@ Q_SIGNALS:
void pausedChanged(bool);
void alwaysRunToEndChanged(bool);
void loopCountChanged(int);
+ Q_REVISION(12) void finished();
public Q_SLOTS:
void restart();
diff --git a/src/quick/util/qquickglobal.cpp b/src/quick/util/qquickglobal.cpp
index 7fa20636ec..5337bfd640 100644
--- a/src/quick/util/qquickglobal.cpp
+++ b/src/quick/util/qquickglobal.cpp
@@ -388,7 +388,7 @@ public:
float matVals[16];
QV4::ScopedValue v(scope);
for (quint32 i = 0; i < 16; ++i) {
- v = array->getIndexed(i);
+ v = array->get(i);
if (!v->isNumber())
return QMatrix4x4();
matVals[i] = v->asDouble();
diff --git a/src/quick/util/qquickimageprovider.cpp b/src/quick/util/qquickimageprovider.cpp
index f7c8724318..5d7664433b 100644
--- a/src/quick/util/qquickimageprovider.cpp
+++ b/src/quick/util/qquickimageprovider.cpp
@@ -39,8 +39,10 @@
#include "qquickimageprovider.h"
+#include "qquickimageprovider_p.h"
#include "qquickpixmapcache_p.h"
#include <QtQuick/private/qsgcontext_p.h>
+#include <private/qqmlglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -160,7 +162,10 @@ QQuickTextureFactory *QQuickTextureFactory::textureFactoryForImage(const QImage
Constructs the image response
*/
QQuickImageResponse::QQuickImageResponse()
+ : QObject(*(new QQuickImageResponsePrivate))
{
+ qmlobject_connect(this, QQuickImageResponse, SIGNAL(finished()),
+ this, QQuickImageResponse, SLOT(_q_finished()));
}
/*!
diff --git a/src/quick/util/qquickimageprovider.h b/src/quick/util/qquickimageprovider.h
index 4f8193789a..82d0501697 100644
--- a/src/quick/util/qquickimageprovider.h
+++ b/src/quick/util/qquickimageprovider.h
@@ -69,6 +69,8 @@ public:
static QQuickTextureFactory *textureFactoryForImage(const QImage &image);
};
+class QQuickImageResponsePrivate;
+
class Q_QUICK_EXPORT QQuickImageResponse : public QObject
{
Q_OBJECT
@@ -84,6 +86,10 @@ public Q_SLOTS:
Q_SIGNALS:
void finished();
+
+private:
+ Q_DECLARE_PRIVATE(QQuickImageResponse)
+ Q_PRIVATE_SLOT(d_func(), void _q_finished())
};
class Q_QUICK_EXPORT QQuickImageProvider : public QQmlImageProviderBase
diff --git a/src/quick/util/qquickimageprovider_p.h b/src/quick/util/qquickimageprovider_p.h
new file mode 100644
index 0000000000..b5baf79319
--- /dev/null
+++ b/src/quick/util/qquickimageprovider_p.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKQQUICKIMAGEPROVIDER_P_H
+#define QQUICKQQUICKIMAGEPROVIDER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtquickglobal_p.h>
+#include <private/qobject_p.h>
+#include <qquickimageprovider.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickImageResponsePrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickImageResponse)
+public:
+ bool finished = false;
+
+ void _q_finished() { finished = true; }
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QQUICKQQUICKIMAGEPROVIDER_P_H
diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp
index 4237ec3edf..0dd2a88ca1 100644
--- a/src/quick/util/qquickpixmapcache.cpp
+++ b/src/quick/util/qquickpixmapcache.cpp
@@ -39,6 +39,7 @@
#include "qquickpixmapcache_p.h"
#include <qquickimageprovider.h>
+#include "qquickimageprovider_p.h"
#include <qqmlengine.h>
#include <private/qqmlglobal_p.h>
@@ -179,6 +180,8 @@ public:
QQuickPixmapReaderThreadObject(QQuickPixmapReader *);
void processJobs();
bool event(QEvent *e) override;
+public slots:
+ void asyncResponseFinished(QQuickImageResponse *response);
private slots:
void networkRequestDone();
void asyncResponseFinished();
@@ -613,10 +616,15 @@ void QQuickPixmapReaderThreadObject::networkRequestDone()
#endif
}
+void QQuickPixmapReaderThreadObject::asyncResponseFinished(QQuickImageResponse *response)
+{
+ reader->asyncResponseFinished(response);
+}
+
void QQuickPixmapReaderThreadObject::asyncResponseFinished()
{
QQuickImageResponse *response = static_cast<QQuickImageResponse *>(sender());
- reader->asyncResponseFinished(response);
+ asyncResponseFinished(response);
}
void QQuickPixmapReader::processJobs()
@@ -800,7 +808,14 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
response = asyncProvider->requestImageResponse(imageId(url), runningJob->requestSize);
}
- QObject::connect(response, SIGNAL(finished()), threadObject, SLOT(asyncResponseFinished()));
+ // Might be that the async provider was so quick it emitted the signal before we
+ // could connect to it.
+ if (static_cast<QQuickImageResponsePrivate*>(QObjectPrivate::get(response))->finished) {
+ QMetaObject::invokeMethod(threadObject, "asyncResponseFinished",
+ Qt::QueuedConnection, Q_ARG(QQuickImageResponse*, response));
+ } else {
+ QObject::connect(response, SIGNAL(finished()), threadObject, SLOT(asyncResponseFinished()));
+ }
asyncResponses.insert(response, runningJob);
break;
diff --git a/src/quick/util/qquickprofiler.cpp b/src/quick/util/qquickprofiler.cpp
index bd9f04e562..decc6eac31 100644
--- a/src/quick/util/qquickprofiler.cpp
+++ b/src/quick/util/qquickprofiler.cpp
@@ -116,9 +116,8 @@ void QQuickProfiler::stopProfilingImpl()
m_data.clear();
}
-void QQuickProfiler::reportDataImpl(bool trackLocations)
+void QQuickProfiler::reportDataImpl()
{
- Q_UNUSED(trackLocations);
QMutexLocker lock(&m_dataMutex);
emit dataReady(m_data);
m_data.clear();
diff --git a/src/quick/util/qquickprofiler_p.h b/src/quick/util/qquickprofiler_p.h
index d699ddf371..28b058c2e8 100644
--- a/src/quick/util/qquickprofiler_p.h
+++ b/src/quick/util/qquickprofiler_p.h
@@ -357,7 +357,7 @@ protected:
void startProfilingImpl(quint64 features);
void stopProfilingImpl();
- void reportDataImpl(bool trackLocations);
+ void reportDataImpl();
void setTimer(const QElapsedTimer &t);
};
diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp
index a3a598621f..9c01a3f80e 100644
--- a/src/quick/util/qquickpropertychanges.cpp
+++ b/src/quick/util/qquickpropertychanges.cpp
@@ -298,7 +298,7 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
}
}
- if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->containsTranslations()) {
+ if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) {
QUrl url = QUrl();
int line = -1;
int column = -1;
@@ -313,7 +313,7 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
QString expression;
QQmlBinding::Identifier id = QQmlBinding::Invalid;
- if (!binding->containsTranslations()) {
+ if (!binding->isTranslationBinding()) {
expression = binding->valueAsString(qmlUnit);
id = binding->value.compiledScriptIndex;
}
@@ -468,7 +468,7 @@ QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions()
QQmlContextData *context = QQmlContextData::get(qmlContext(this));
QQmlBinding *newBinding = nullptr;
- if (e.binding && e.binding->containsTranslations()) {
+ if (e.binding && e.binding->isTranslationBinding()) {
newBinding = QQmlBinding::createTranslationBinding(d->compilationUnit, e.binding, object(), context);
} else if (e.id != QQmlBinding::Invalid) {
QV4::Scope scope(qmlEngine(this)->handle());
diff --git a/src/quick/util/qquickstate.cpp b/src/quick/util/qquickstate.cpp
index b953ad1c7f..3ca6440784 100644
--- a/src/quick/util/qquickstate.cpp
+++ b/src/quick/util/qquickstate.cpp
@@ -222,7 +222,7 @@ bool QQuickState::isWhenKnown() const
QQmlBinding *QQuickState::when() const
{
Q_D(const QQuickState);
- return d->when;
+ return d->when.data();
}
void QQuickState::setWhen(QQmlBinding *when)
diff --git a/src/quick/util/qquickstate_p_p.h b/src/quick/util/qquickstate_p_p.h
index f1bc24c558..61472b4d06 100644
--- a/src/quick/util/qquickstate_p_p.h
+++ b/src/quick/util/qquickstate_p_p.h
@@ -203,12 +203,12 @@ class QQuickStatePrivate : public QObjectPrivate
public:
QQuickStatePrivate()
- : when(nullptr), named(false), inState(false), group(nullptr) {}
+ : named(false), inState(false), group(nullptr) {}
typedef QList<QQuickSimpleAction> SimpleActionList;
QString name;
- QQmlBinding *when;
+ QQmlBinding::Ptr when;
bool named;
struct OperationGuard : public QQmlGuard<QQuickStateOperation>
diff --git a/src/quick/util/qquicksvgparser.cpp b/src/quick/util/qquicksvgparser.cpp
index 2bf9c121d3..0687913565 100644
--- a/src/quick/util/qquicksvgparser.cpp
+++ b/src/quick/util/qquicksvgparser.cpp
@@ -280,11 +280,8 @@ bool QQuickSvgParser::parsePathDataFast(const QString &dataStr, QPainterPath &pa
++str;
QChar pathElem = *str;
++str;
- QChar endc = *end;
- *const_cast<QChar *>(end) = 0; // parseNumbersArray requires 0-termination that QStringRef cannot guarantee
QVarLengthArray<qreal, 8> arg;
parseNumbersArray(str, arg);
- *const_cast<QChar *>(end) = endc;
if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z'))
arg.append(0);//dummy
const qreal *num = arg.constData();
diff --git a/src/quick/util/qquickutilmodule.cpp b/src/quick/util/qquickutilmodule.cpp
index b2e5b84cf4..31d4d4c437 100644
--- a/src/quick/util/qquickutilmodule.cpp
+++ b/src/quick/util/qquickutilmodule.cpp
@@ -132,4 +132,7 @@ void QQuickUtilModule::defineModule()
qmlRegisterType<QQuickShortcut,9>("QtQuick", 2, 9, "Shortcut");
#endif
+
+ qmlRegisterUncreatableType<QQuickAbstractAnimation, 12>("QtQuick", 2, 12, "Animation",
+ QQuickAbstractAnimation::tr("Animation is an abstract class"));
}
diff --git a/src/quick/util/util.pri b/src/quick/util/util.pri
index edcb268cd9..c51f082d03 100644
--- a/src/quick/util/util.pri
+++ b/src/quick/util/util.pri
@@ -53,6 +53,7 @@ HEADERS += \
$$PWD/qquickfontloader_p.h \
$$PWD/qquickstyledtext_p.h \
$$PWD/qquickimageprovider.h \
+ $$PWD/qquickimageprovider_p.h \
$$PWD/qquicksvgparser_p.h \
$$PWD/qquickvaluetypes_p.h \
$$PWD/qquickanimator_p.h \