aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick')
-rw-r--r--src/quick/accessible/qaccessiblequickitem.cpp3
-rw-r--r--src/quick/designer/qquickdesignersupport.cpp5
-rw-r--r--src/quick/doc/images/declarative-arcrotation.pngbin0 -> 4315 bytes
-rw-r--r--src/quick/doc/images/pathitem-code-example.pngbin0 -> 5989 bytes
-rw-r--r--src/quick/doc/images/shape-radial-gradient.pngbin0 -> 16523 bytes
-rw-r--r--src/quick/doc/images/visualpath-code-example.pngbin0 -> 844 bytes
-rw-r--r--src/quick/doc/snippets/qml/layerblending.qml2
-rw-r--r--src/quick/doc/snippets/qml/opacitymask.qml2
-rw-r--r--src/quick/doc/snippets/qml/path/arcrotation.qml52
-rw-r--r--src/quick/doc/src/qmltypereference.qdoc1
-rw-r--r--src/quick/handlers/handlers.pri20
-rw-r--r--src/quick/handlers/qquickdraghandler.cpp210
-rw-r--r--src/quick/handlers/qquickdraghandler_p.h137
-rw-r--r--src/quick/handlers/qquickhandlersmodule.cpp99
-rw-r--r--src/quick/handlers/qquickhandlersmodule_p.h (renamed from src/quick/scenegraph/util/qsgdistancefieldutil_p.h)41
-rw-r--r--src/quick/handlers/qquickmultipointerhandler.cpp248
-rw-r--r--src/quick/handlers/qquickmultipointerhandler_p.h112
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp311
-rw-r--r--src/quick/handlers/qquickpinchhandler_p.h173
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler.cpp134
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler_p.h96
-rw-r--r--src/quick/handlers/qquickpointerhandler.cpp255
-rw-r--r--src/quick/handlers/qquickpointerhandler_p.h125
-rw-r--r--src/quick/handlers/qquickpointersinglehandler.cpp243
-rw-r--r--src/quick/handlers/qquickpointersinglehandler_p.h154
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp360
-rw-r--r--src/quick/handlers/qquicktaphandler_p.h132
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp30
-rw-r--r--src/quick/items/qquickanimatedimage_p.h1
-rw-r--r--src/quick/items/qquickevents.cpp549
-rw-r--r--src/quick/items/qquickevents_p_p.h137
-rw-r--r--src/quick/items/qquickflickable.cpp127
-rw-r--r--src/quick/items/qquickflickable_p.h11
-rw-r--r--src/quick/items/qquickflickable_p_p.h1
-rw-r--r--src/quick/items/qquickgenericshadereffect.cpp3
-rw-r--r--src/quick/items/qquickimage.cpp35
-rw-r--r--src/quick/items/qquickimage_p_p.h2
-rw-r--r--src/quick/items/qquickimagebase.cpp65
-rw-r--r--src/quick/items/qquickimagebase_p.h1
-rw-r--r--src/quick/items/qquickimagebase_p_p.h4
-rw-r--r--src/quick/items/qquickimplicitsizeitem.cpp26
-rw-r--r--src/quick/items/qquickimplicitsizeitem_p.h8
-rw-r--r--src/quick/items/qquickimplicitsizeitem_p_p.h3
-rw-r--r--src/quick/items/qquickitem.cpp180
-rw-r--r--src/quick/items/qquickitem.h7
-rw-r--r--src/quick/items/qquickitem_p.h22
-rw-r--r--src/quick/items/qquickitemanimation.cpp2
-rw-r--r--src/quick/items/qquickitemchangelistener_p.h1
-rw-r--r--src/quick/items/qquickitemsmodule.cpp12
-rw-r--r--src/quick/items/qquickmousearea.cpp1
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp1
-rw-r--r--src/quick/items/qquickopenglshadereffect.cpp28
-rw-r--r--src/quick/items/qquickopenglshadereffectnode.cpp15
-rw-r--r--src/quick/items/qquickopenglshadereffectnode_p.h2
-rw-r--r--src/quick/items/qquickpincharea.cpp1
-rw-r--r--src/quick/items/qquickpositioners.cpp9
-rw-r--r--src/quick/items/qquickpositioners_p.h1
-rw-r--r--src/quick/items/qquickpositioners_p_p.h3
-rw-r--r--src/quick/items/qquickrectangle.cpp12
-rw-r--r--src/quick/items/qquickrectangle_p_p.h15
-rw-r--r--src/quick/items/qquickscreen.cpp48
-rw-r--r--src/quick/items/qquickscreen_p.h9
-rw-r--r--src/quick/items/qquickshadereffectsource.cpp40
-rw-r--r--src/quick/items/qquickshadereffectsource_p.h6
-rw-r--r--src/quick/items/qquickstateoperations.cpp2
-rw-r--r--src/quick/items/qquicktext.cpp87
-rw-r--r--src/quick/items/qquicktext_p.h2
-rw-r--r--src/quick/items/qquicktext_p_p.h4
-rw-r--r--src/quick/items/qquicktextedit.cpp39
-rw-r--r--src/quick/items/qquicktextedit_p.h5
-rw-r--r--src/quick/items/qquicktextinput.cpp13
-rw-r--r--src/quick/items/qquickwindow.cpp545
-rw-r--r--src/quick/items/qquickwindow.h4
-rw-r--r--src/quick/items/qquickwindow_p.h25
-rw-r--r--src/quick/items/qquickwindowmodule.cpp1
-rw-r--r--src/quick/quick.pro1
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h1
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp8
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp21
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp4
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgpkmhandler.cpp209
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgpkmhandler_p.h94
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer.cpp22
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer_p.h2
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode.cpp9
-rw-r--r--src/quick/scenegraph/coreapi/qsgshaderrewriter.cpp1
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer.cpp6
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h8
-rw-r--r--src/quick/scenegraph/qsgcontext.cpp9
-rw-r--r--src/quick/scenegraph/qsgcontext_p.h3
-rw-r--r--src/quick/scenegraph/qsgcontextplugin.cpp11
-rw-r--r--src/quick/scenegraph/qsgdefaultcontext.cpp5
-rw-r--r--src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp20
-rw-r--r--src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h2
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.cpp20
-rw-r--r--src/quick/scenegraph/qsgdefaultinternalimagenode.cpp6
-rw-r--r--src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp6
-rw-r--r--src/quick/scenegraph/qsgdefaultlayer.cpp20
-rw-r--r--src/quick/scenegraph/qsgdefaultlayer_p.h4
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext.cpp37
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext_p.h2
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode.cpp16
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp68
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode_p.h2
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp24
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp4
-rw-r--r--src/quick/scenegraph/scenegraph.pri17
-rw-r--r--src/quick/scenegraph/util/qsgdistancefieldutil.cpp115
-rw-r--r--src/quick/scenegraph/util/qsgflatcolormaterial.cpp6
-rw-r--r--src/quick/scenegraph/util/qsgtexture.cpp24
-rw-r--r--src/quick/scenegraph/util/qsgtexture.h3
-rw-r--r--src/quick/scenegraph/util/qsgtexture_p.h4
-rw-r--r--src/quick/scenegraph/util/qsgtexturematerial.cpp4
-rw-r--r--src/quick/scenegraph/util/qsgtexturereader.cpp82
-rw-r--r--src/quick/scenegraph/util/qsgtexturereader_p.h72
-rw-r--r--src/quick/scenegraph/util/qsgvertexcolormaterial.cpp6
-rw-r--r--src/quick/util/qquickanimatorjob.cpp25
-rw-r--r--src/quick/util/qquickanimatorjob_p.h1
-rw-r--r--src/quick/util/qquickglobal.cpp36
-rw-r--r--src/quick/util/qquickimageprovider.cpp14
-rw-r--r--src/quick/util/qquickpath.cpp184
-rw-r--r--src/quick/util/qquickpath_p.h19
-rw-r--r--src/quick/util/qquickpath_p_p.h2
-rw-r--r--src/quick/util/qquickpixmapcache.cpp49
-rw-r--r--src/quick/util/qquicksvgparser.cpp8
-rw-r--r--src/quick/util/qquicksvgparser_p.h7
-rw-r--r--src/quick/util/qquickvaluetypes.cpp10
-rw-r--r--src/quick/util/qquickvaluetypes_p.h4
130 files changed, 5560 insertions, 841 deletions
diff --git a/src/quick/accessible/qaccessiblequickitem.cpp b/src/quick/accessible/qaccessiblequickitem.cpp
index fbde5d354d..2d6bb02af4 100644
--- a/src/quick/accessible/qaccessiblequickitem.cpp
+++ b/src/quick/accessible/qaccessiblequickitem.cpp
@@ -142,9 +142,6 @@ QAccessibleInterface *QAccessibleQuickItem::child(int index) const
return 0;
QQuickItem *child = children.at(index);
- if (!child) // FIXME can this happen?
- return 0;
-
return QAccessible::queryAccessibleInterface(child);
}
diff --git a/src/quick/designer/qquickdesignersupport.cpp b/src/quick/designer/qquickdesignersupport.cpp
index 749ece8221..88971e3172 100644
--- a/src/quick/designer/qquickdesignersupport.cpp
+++ b/src/quick/designer/qquickdesignersupport.cpp
@@ -90,10 +90,11 @@ void QQuickDesignerSupport::refFromEffectItem(QQuickItem *referencedItem, bool h
QSGRenderContext *rc = QQuickWindowPrivate::get(referencedItem->window())->context;
QSGLayer *texture = rc->sceneGraphContext()->createLayer(rc);
+ QSizeF itemSize = referencedItem->size();
texture->setLive(true);
texture->setItem(QQuickItemPrivate::get(referencedItem)->rootNode());
- texture->setRect(referencedItem->boundingRect());
- texture->setSize(referencedItem->boundingRect().size().toSize());
+ texture->setRect(QRectF(QPointF(0, 0), itemSize));
+ texture->setSize(itemSize.toSize());
texture->setRecursive(true);
#if QT_CONFIG(opengl)
#ifndef QT_OPENGL_ES
diff --git a/src/quick/doc/images/declarative-arcrotation.png b/src/quick/doc/images/declarative-arcrotation.png
new file mode 100644
index 0000000000..03f009bc12
--- /dev/null
+++ b/src/quick/doc/images/declarative-arcrotation.png
Binary files differ
diff --git a/src/quick/doc/images/pathitem-code-example.png b/src/quick/doc/images/pathitem-code-example.png
new file mode 100644
index 0000000000..25dbe8b311
--- /dev/null
+++ b/src/quick/doc/images/pathitem-code-example.png
Binary files differ
diff --git a/src/quick/doc/images/shape-radial-gradient.png b/src/quick/doc/images/shape-radial-gradient.png
new file mode 100644
index 0000000000..bfff2e4b6b
--- /dev/null
+++ b/src/quick/doc/images/shape-radial-gradient.png
Binary files differ
diff --git a/src/quick/doc/images/visualpath-code-example.png b/src/quick/doc/images/visualpath-code-example.png
new file mode 100644
index 0000000000..429e85aa32
--- /dev/null
+++ b/src/quick/doc/images/visualpath-code-example.png
Binary files differ
diff --git a/src/quick/doc/snippets/qml/layerblending.qml b/src/quick/doc/snippets/qml/layerblending.qml
index a922f7896c..3e75a607a6 100644
--- a/src/quick/doc/snippets/qml/layerblending.qml
+++ b/src/quick/doc/snippets/qml/layerblending.qml
@@ -62,7 +62,7 @@ Item {
uniform highp vec2 pixelSize;
varying highp vec2 qt_TexCoord0;
void main() {
- highp vec2 tc = sign(sin(3.14152 * qt_TexCoord0 * pixelSize));
+ highp vec2 tc = sign(sin(3.14159265358979323846 * qt_TexCoord0 * pixelSize));
if (tc.x != tc.y)
gl_FragColor = color1;
else
diff --git a/src/quick/doc/snippets/qml/opacitymask.qml b/src/quick/doc/snippets/qml/opacitymask.qml
index beffb633d6..beb5cc6f67 100644
--- a/src/quick/doc/snippets/qml/opacitymask.qml
+++ b/src/quick/doc/snippets/qml/opacitymask.qml
@@ -63,7 +63,7 @@ Item {
uniform highp vec2 pixelSize;
varying highp vec2 qt_TexCoord0;
void main() {
- highp vec2 tc = sign(sin(3.14152 * qt_TexCoord0 * pixelSize));
+ highp vec2 tc = sign(sin(3.14159265358979323846 * qt_TexCoord0 * pixelSize));
if (tc.x != tc.y)
gl_FragColor = color1;
else
diff --git a/src/quick/doc/snippets/qml/path/arcrotation.qml b/src/quick/doc/snippets/qml/path/arcrotation.qml
new file mode 100644
index 0000000000..c73d67ff17
--- /dev/null
+++ b/src/quick/doc/snippets/qml/path/arcrotation.qml
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.9
+//![0]
+Path {
+ startX: 50; startY: 100
+
+ PathArc {
+ x: 150; y: 100
+ radiusX: 50; radiusY: 20
+ xAxisRotation: 45
+ }
+}
+//![0]
diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc
index b0aa143505..7c7bdbc137 100644
--- a/src/quick/doc/src/qmltypereference.qdoc
+++ b/src/quick/doc/src/qmltypereference.qdoc
@@ -170,6 +170,7 @@ available when you import \c QtQuick.
\li \l enumeration \c font.capitalization
\li \l real \c font.letterSpacing
\li \l real \c font.wordSpacing
+ \li \l bool \c font.kerning
\li \l enumeration \c font.hintingPreference
\endlist
diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri
new file mode 100644
index 0000000000..17066bb33a
--- /dev/null
+++ b/src/quick/handlers/handlers.pri
@@ -0,0 +1,20 @@
+HEADERS += \
+ $$PWD/qquickdraghandler_p.h \
+ $$PWD/qquickhandlersmodule_p.h \
+ $$PWD/qquickmultipointerhandler_p.h \
+ $$PWD/qquickpinchhandler_p.h \
+ $$PWD/qquickpointerdevicehandler_p.h \
+ $$PWD/qquickpointerhandler_p.h \
+ $$PWD/qquickpointersinglehandler_p.h \
+ $$PWD/qquicktaphandler_p.h \
+
+SOURCES += \
+ $$PWD/qquickdraghandler.cpp \
+ $$PWD/qquickhandlersmodule.cpp \
+ $$PWD/qquickmultipointerhandler.cpp \
+ $$PWD/qquickpinchhandler.cpp \
+ $$PWD/qquickpointerdevicehandler.cpp \
+ $$PWD/qquickpointerhandler.cpp \
+ $$PWD/qquickpointersinglehandler.cpp \
+ $$PWD/qquicktaphandler.cpp \
+
diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp
new file mode 100644
index 0000000000..b41e1f6c9d
--- /dev/null
+++ b/src/quick/handlers/qquickdraghandler.cpp
@@ -0,0 +1,210 @@
+/****************************************************************************
+**
+** 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 "qquickdraghandler_p.h"
+#include <private/qquickwindow_p.h>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype DragHandler
+ \instantiates QQuickDragHandler
+ \inqmlmodule QtQuick
+ \ingroup qtquick-handlers
+ \brief Handler for dragging
+
+ DragHandler is a handler that is used to interactively move an Item.
+
+ At this time, drag-and-drop is not yet supported.
+
+ \sa Drag, MouseArea
+*/
+
+QQuickDragHandler::QQuickDragHandler(QObject *parent)
+ : QQuickPointerSingleHandler(parent)
+{
+}
+
+QQuickDragHandler::~QQuickDragHandler()
+{
+}
+
+bool QQuickDragHandler::wantsEventPoint(QQuickEventPoint *point)
+{
+ // If we've already been interested in a point, stay interested, even if it has strayed outside bounds.
+ return ((point->state() != QQuickEventPoint::Pressed && this->point().id() == point->pointId())
+ || QQuickPointerSingleHandler::wantsEventPoint(point));
+}
+
+void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point)
+{
+ if (grabber == this && stateChange == QQuickEventPoint::GrabExclusive)
+ // In case the grab got handled over from another grabber, we might not get the Press
+ initializeTargetStartPos(point);
+ enforceConstraints();
+ QQuickPointerSingleHandler::onGrabChanged(grabber, stateChange, point);
+}
+
+void QQuickDragHandler::onActiveChanged()
+{
+ if (!active())
+ m_targetStartPos = QPointF();
+}
+
+void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point)
+{
+ point->setAccepted();
+ switch (point->state()) {
+ case QQuickEventPoint::Pressed:
+ initializeTargetStartPos(point);
+ setPassiveGrab(point);
+ break;
+ case QQuickEventPoint::Updated: {
+ QPointF delta = point->scenePos() - point->scenePressPos();
+ if (!m_xAxis.enabled())
+ delta.setX(0);
+ if (!m_yAxis.enabled())
+ delta.setY(0);
+ if (active()) {
+ setTranslation(delta);
+ if (target() && target()->parentItem()) {
+ QPointF pos = target()->parentItem()->mapFromScene(m_targetStartPos + delta);
+ enforceAxisConstraints(&pos);
+ moveTarget(pos, point);
+ }
+ } else if (!point->exclusiveGrabber() &&
+ ((m_xAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point)) ||
+ (m_yAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point)))) {
+ setExclusiveGrab(point);
+ if (auto parent = parentItem()) {
+ if (point->pointerEvent()->asPointerTouchEvent())
+ parent->setKeepTouchGrab(true);
+ // tablet and mouse are treated the same by Item's legacy event handling, and
+ // touch becomes synth-mouse for Flickable, so we need to prevent stealing
+ // mouse grab too, whenever dragging occurs in an enabled direction
+ parent->setKeepMouseGrab(true);
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+}
+
+void QQuickDragHandler::enforceConstraints()
+{
+ if (!target() || !target()->parentItem())
+ return;
+ QPointF pos = target()->position();
+ QPointF copy(pos);
+ enforceAxisConstraints(&pos);
+ if (pos != copy)
+ target()->setPosition(pos);
+}
+
+void QQuickDragHandler::enforceAxisConstraints(QPointF *localPos)
+{
+ if (m_xAxis.enabled())
+ localPos->setX(qBound(m_xAxis.minimum(), localPos->x(), m_xAxis.maximum()));
+ if (m_yAxis.enabled())
+ localPos->setY(qBound(m_yAxis.minimum(), localPos->y(), m_yAxis.maximum()));
+}
+
+void QQuickDragHandler::initializeTargetStartPos(QQuickEventPoint *point)
+{
+ if (target() && target()->parentItem() && m_targetStartPos.isNull()) { // prefer the m_targetStartPos we got when it got Pressed.
+ m_targetStartPos = target()->parentItem()->mapToScene(target()->position());
+ if (!target()->contains(point->pos())) {
+ // If pressed outside of target item, move the target item so that the touchpoint is in its center,
+ // while still respecting the axis constraints.
+ const QPointF center = target()->parentItem()->mapFromScene(point->scenePos());
+ const QPointF pointCenteredInItemPos = target()->parentItem()->mapToScene(center - QPointF(target()->width(), target()->height())/2);
+ if (m_xAxis.enabled())
+ m_targetStartPos.setX(pointCenteredInItemPos.x());
+ if (m_yAxis.enabled())
+ m_targetStartPos.setY(pointCenteredInItemPos.y());
+ }
+ }
+}
+
+void QQuickDragHandler::setTranslation(const QPointF &trans)
+{
+ if (trans == m_translation) // fuzzy compare?
+ return;
+ m_translation = trans;
+ emit translationChanged();
+}
+
+
+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();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h
new file mode 100644
index 0000000000..54fef697f7
--- /dev/null
+++ b/src/quick/handlers/qquickdraghandler_p.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** 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 QQUICKDRAGHANDLER_H
+#define QQUICKDRAGHANDLER_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 "qquickpointersinglehandler_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 QQuickPointerSingleHandler
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT)
+ Q_PROPERTY(QQuickDragAxis * yAxis READ yAxis CONSTANT)
+ Q_PROPERTY(QPointF translation READ translation NOTIFY translationChanged)
+
+public:
+ explicit QQuickDragHandler(QObject *parent = 0);
+ ~QQuickDragHandler();
+
+ void handleEventPoint(QQuickEventPoint *point) override;
+
+ QQuickDragAxis *xAxis() { return &m_xAxis; }
+ QQuickDragAxis *yAxis() { return &m_yAxis; }
+
+ QPointF translation() const { return m_translation; }
+ void setTranslation(const QPointF &trans);
+
+ Q_INVOKABLE void enforceConstraints();
+
+Q_SIGNALS:
+// void gestureStarted(QQuickGestureEvent *gesture);
+ void translationChanged();
+
+protected:
+ bool wantsEventPoint(QQuickEventPoint *point) override;
+ void onActiveChanged() override;
+ void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override;
+
+private:
+ void ungrab();
+ void enforceAxisConstraints(QPointF *localPos);
+ void initializeTargetStartPos(QQuickEventPoint *point);
+
+private:
+ QPointF m_targetStartPos;
+ QPointF m_translation;
+ QQuickDragAxis m_xAxis;
+ QQuickDragAxis m_yAxis;
+
+ friend class QQuickDragAxis;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickDragHandler)
+QML_DECLARE_TYPE(QQuickDragAxis)
+
+#endif // QQUICKDRAGHANDLER_H
diff --git a/src/quick/handlers/qquickhandlersmodule.cpp b/src/quick/handlers/qquickhandlersmodule.cpp
new file mode 100644
index 0000000000..4a3a1f6aa2
--- /dev/null
+++ b/src/quick/handlers/qquickhandlersmodule.cpp
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** 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 "qquickhandlersmodule_p.h"
+#include "qquickpointerhandler_p.h"
+#include "qquickdraghandler_p.h"
+#include "qquickpinchhandler_p.h"
+#include "qquicktaphandler_p.h"
+
+static void initResources()
+{
+#ifdef QT_STATIC
+ Q_INIT_RESOURCE(qmake_Qt_labs_handlers);
+#endif
+}
+
+QT_BEGIN_NAMESPACE
+
+static QQmlPrivate::AutoParentResult handler_autoParent(QObject *obj, QObject *parent)
+{
+ if (qmlobject_cast<QQuickItem *>(parent)) {
+ QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(obj);
+ if (handler) {
+ handler->setParent(parent);
+ return QQmlPrivate::Parented;
+ }
+ }
+ return QQmlPrivate::IncompatibleObject;
+}
+
+static void qt_quickhandlers_defineModule(const char *uri, int major, int minor)
+{
+ QQmlPrivate::RegisterAutoParent autoparent = { 0, &handler_autoParent };
+ QQmlPrivate::qmlregister(QQmlPrivate::AutoParentRegistration, &autoparent);
+ qmlRegisterUncreatableType<QQuickPointerEvent>(uri, major, minor, "PointerEvent",
+ QQuickPointerHandler::tr("PointerEvent is only available as a parameter of several signals in PointerHandler"));
+ qmlRegisterUncreatableType<QQuickPointerDevice>(uri, major, minor, "PointerDevice",
+ QQuickPointerHandler::tr("PointerDevice is only available as a property of PointerEvent"));
+ qRegisterMetaType<QPointingDeviceUniqueId>("QPointingDeviceUniqueId");
+ qmlRegisterUncreatableType<QPointingDeviceUniqueId>(uri, major, minor, "PointingDeviceUniqueId",
+ QQuickPointerHandler::tr("PointingDeviceUniqueId is only available as a property of PointerEvent"));
+
+ qmlRegisterType<QQuickPointerHandler>(uri,major,minor,"PointerHandler");
+ 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<QQuickPinchHandler>(uri,major,minor,"PinchHandler");
+ qmlRegisterType<QQuickTapHandler>(uri,major,minor,"TapHandler");
+ qRegisterMetaType<QQuickHandlerPoint>();
+}
+
+void QQuickHandlersModule::defineModule()
+{
+ initResources();
+
+ const char uri[] = "Qt.labs.handlers";
+ int majorVersion = 1;
+ int minorVersion = 0;
+
+ qt_quickhandlers_defineModule(uri, majorVersion, minorVersion);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgdistancefieldutil_p.h b/src/quick/handlers/qquickhandlersmodule_p.h
index 354a48a81e..7eb8d39b98 100644
--- a/src/quick/scenegraph/util/qsgdistancefieldutil_p.h
+++ b/src/quick/handlers/qquickhandlersmodule_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QSGDISTANCEFIELDUTIL_H
-#define QSGDISTANCEFIELDUTIL_H
+#ifndef QQUICKHANDLERSMODULE_P_H
+#define QQUICKHANDLERSMODULE_P_H
//
// W A R N I N G
@@ -51,43 +51,18 @@
// We mean it.
//
-#include <qrawfont.h>
-#include <private/qfontengine_p.h>
-#include <private/qsgadaptationlayer_p.h>
+#include <qqml.h>
+#include <private/qtquickglobal_p.h>
QT_BEGIN_NAMESPACE
-typedef float (*ThresholdFunc)(float glyphScale);
-typedef float (*AntialiasingSpreadFunc)(float glyphScale);
-
-class QOpenGLShaderProgram;
-class QSGDistanceFieldGlyphCache;
-class QSGContext;
-
-class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldGlyphCacheManager
+class Q_QUICK_PRIVATE_EXPORT QQuickHandlersModule
{
public:
- QSGDistanceFieldGlyphCacheManager();
- ~QSGDistanceFieldGlyphCacheManager();
-
- QSGDistanceFieldGlyphCache *cache(const QRawFont &font);
- void insertCache(const QRawFont &font, QSGDistanceFieldGlyphCache *cache);
-
- ThresholdFunc thresholdFunc() const { return m_threshold_func; }
- void setThresholdFunc(ThresholdFunc func) { m_threshold_func = func; }
-
- AntialiasingSpreadFunc antialiasingSpreadFunc() const { return m_antialiasingSpread_func; }
- void setAntialiasingSpreadFunc(AntialiasingSpreadFunc func) { m_antialiasingSpread_func = func; }
-
-private:
- static QString fontKey(const QRawFont &font);
-
- QHash<QString, QSGDistanceFieldGlyphCache *> m_caches;
-
- ThresholdFunc m_threshold_func;
- AntialiasingSpreadFunc m_antialiasingSpread_func;
+ static void defineModule();
};
QT_END_NAMESPACE
-#endif // QSGDISTANCEFIELDUTIL_H
+#endif // QQUICKHANDLERSMODULE_P_H
+
diff --git a/src/quick/handlers/qquickmultipointerhandler.cpp b/src/quick/handlers/qquickmultipointerhandler.cpp
new file mode 100644
index 0000000000..a605b3f12e
--- /dev/null
+++ b/src/quick/handlers/qquickmultipointerhandler.cpp
@@ -0,0 +1,248 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "qquickmultipointerhandler_p.h"
+#include <private/qquickitem_p.h>
+#include <QLineF>
+#include <QMouseEvent>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ An intermediate class (not registered as a QML type)
+ for the type of handler which requires and acts upon a specific number
+ of multiple touchpoints.
+*/
+QQuickMultiPointerHandler::QQuickMultiPointerHandler(QObject *parent, int requiredPointCount)
+ : QQuickPointerDeviceHandler(parent)
+ , m_requiredPointCount(requiredPointCount)
+ , m_pointDistanceThreshold(0)
+{
+}
+
+QQuickMultiPointerHandler::~QQuickMultiPointerHandler()
+{
+}
+
+bool QQuickMultiPointerHandler::wantsPointerEvent(QQuickPointerEvent *event)
+{
+ if (!QQuickPointerDeviceHandler::wantsPointerEvent(event))
+ return false;
+
+ if (sameAsCurrentPoints(event))
+ return true;
+
+ const QVector<QQuickEventPoint *> candidatePoints = eligiblePoints(event);
+ const bool ret = (candidatePoints.size() == m_requiredPointCount);
+ if (ret)
+ m_currentPoints = candidatePoints;
+ return ret;
+}
+
+QVector<QQuickEventPoint *> QQuickMultiPointerHandler::eligiblePoints(QQuickPointerEvent *event)
+{
+ QVector<QQuickEventPoint *> ret;
+ int c = event->pointCount();
+ QRectF targetBounds = target()->mapRectToScene(target()->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.
+ bool stealingAllowed = event->isPressEvent() || event->isReleaseEvent();
+ for (int i = 0; i < c; ++i) {
+ QQuickEventPoint *p = event->point(i);
+ if (!stealingAllowed) {
+ QObject *exclusiveGrabber = p->exclusiveGrabber();
+ if (exclusiveGrabber && exclusiveGrabber != this)
+ continue;
+ }
+ if (p->state() != QQuickEventPoint::Released && targetBounds.contains(p->scenePos()))
+ ret << p;
+ }
+ return ret;
+}
+
+void QQuickMultiPointerHandler::setRequiredPointCount(int c)
+{
+ if (m_requiredPointCount == c)
+ return;
+
+ m_requiredPointCount = c;
+ emit requiredPointCountChanged();
+}
+
+void QQuickMultiPointerHandler::setPointDistanceThreshold(qreal pointDistanceThreshold)
+{
+ if (m_pointDistanceThreshold == pointDistanceThreshold)
+ return;
+
+ m_pointDistanceThreshold = pointDistanceThreshold;
+ emit pointDistanceThresholdChanged();
+}
+
+bool QQuickMultiPointerHandler::sameAsCurrentPoints(QQuickPointerEvent *event)
+{
+ bool ret = true;
+ int c = event->pointCount();
+ if (c != m_currentPoints.size())
+ return false;
+ // TODO optimize: either ensure the points are sorted,
+ // or use std::equal with a predicate
+ for (int i = 0; ret && i < c; ++i) {
+ if (event->point(i)->state() == QQuickEventPoint::Released)
+ return false;
+ bool found = false;
+ int pointId = event->point(i)->pointId();
+ for (QQuickEventPoint *o : qAsConst(m_currentPoints))
+ if (o && pointId == o->pointId())
+ found = true;
+ if (!found)
+ ret = false;
+ }
+ return ret;
+}
+
+// TODO make templates for these functions somehow?
+QPointF QQuickMultiPointerHandler::touchPointCentroid()
+{
+ QPointF ret;
+ if (Q_UNLIKELY(m_currentPoints.size() == 0))
+ return ret;
+ for (QQuickEventPoint *point : qAsConst(m_currentPoints))
+ ret += point->scenePos();
+ return ret / m_currentPoints.size();
+}
+
+QVector2D QQuickMultiPointerHandler::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 QQuickMultiPointerHandler::averageTouchPointDistance(const QPointF &ref)
+{
+ qreal ret = 0;
+ if (Q_UNLIKELY(m_currentPoints.size() == 0))
+ return ret;
+ for (QQuickEventPoint *point : qAsConst(m_currentPoints))
+ ret += QVector2D(point->scenePos() - ref).length();
+ return ret / m_currentPoints.size();
+}
+
+qreal QQuickMultiPointerHandler::averageStartingDistance(const QPointF &ref)
+{
+ // TODO cache it in setActive()?
+ qreal ret = 0;
+ if (Q_UNLIKELY(m_currentPoints.size() == 0))
+ return ret;
+ for (QQuickEventPoint *point : qAsConst(m_currentPoints))
+ ret += QVector2D(point->sceneGrabPos() - ref).length();
+ return ret / m_currentPoints.size();
+}
+
+QVector<QQuickMultiPointerHandler::PointData> QQuickMultiPointerHandler::angles(const QPointF &ref) const
+{
+ QVector<PointData> angles;
+ angles.reserve(m_currentPoints.count());
+ for (QQuickEventPoint *point : qAsConst(m_currentPoints)) {
+ qreal angle = QLineF(ref, point->scenePos()).angle();
+ angles.append(PointData(point->pointId(), -angle)); // convert to clockwise, to be consistent with QQuickItem::rotation
+ }
+ return angles;
+}
+
+qreal QQuickMultiPointerHandler::averageAngleDelta(const QVector<PointData> &old, const QVector<PointData> &newAngles)
+{
+ qreal avgAngleDelta = 0;
+ int numSamples = 0;
+
+ auto oldBegin = old.constBegin();
+
+ for (PointData newData : newAngles) {
+ quint64 id = newData.id;
+ auto it = std::find_if(oldBegin, old.constEnd(), [id] (PointData pd) { return pd.id == id; });
+ qreal angleD = 0;
+ if (it != old.constEnd()) {
+ PointData oldData = *it;
+ // We might rotate from 359 degrees to 1 degree. However, this
+ // should be interpreted as a rotation of +2 degrees instead of
+ // -358 degrees. Therefore, we call remainder() to translate the angle
+ // to be in the range [-180, 180] (-350 to +10 etc)
+ angleD = remainder(newData.angle - oldData.angle, qreal(360));
+ // optimization: narrow down the O(n^2) search to optimally O(n)
+ // if both vectors have the same points and they are in the same order
+ if (it == oldBegin)
+ ++oldBegin;
+ numSamples++;
+ }
+ avgAngleDelta += angleD;
+ }
+ if (numSamples > 1)
+ avgAngleDelta /= numSamples;
+
+ return avgAngleDelta;
+}
+
+void QQuickMultiPointerHandler::acceptPoints(const QVector<QQuickEventPoint *> &points)
+{
+ for (QQuickEventPoint* point : points)
+ point->setAccepted();
+}
+
+bool QQuickMultiPointerHandler::grabPoints(QVector<QQuickEventPoint *> points)
+{
+ bool canGrab = true;
+ for (QQuickEventPoint* point : points) {
+ auto grabber = point->grabberItem();
+ if (grabber && (grabber->keepMouseGrab() || grabber->keepTouchGrab()))
+ canGrab = false;
+ }
+ if (canGrab) {
+ for (QQuickEventPoint* point : points)
+ setExclusiveGrab(point);
+ }
+ return canGrab;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickmultipointerhandler_p.h b/src/quick/handlers/qquickmultipointerhandler_p.h
new file mode 100644
index 0000000000..1bbc2f2fa3
--- /dev/null
+++ b/src/quick/handlers/qquickmultipointerhandler_p.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** 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 QQUICKPOINTERMULTIHANDLER_H
+#define QQUICKPOINTERMULTIHANDLER_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 "qquickpointerdevicehandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickMultiPointerHandler : public QQuickPointerDeviceHandler
+{
+ Q_OBJECT
+ Q_PROPERTY(int requiredPointCount READ requiredPointCount WRITE setRequiredPointCount NOTIFY requiredPointCountChanged)
+ Q_PROPERTY(qreal pointDistanceThreshold READ pointDistanceThreshold WRITE setPointDistanceThreshold NOTIFY pointDistanceThresholdChanged)
+
+public:
+ explicit QQuickMultiPointerHandler(QObject *parent = 0, int requiredPointCount = 2);
+ ~QQuickMultiPointerHandler();
+
+ int requiredPointCount() const { return m_requiredPointCount; }
+ void setRequiredPointCount(int c);
+
+ qreal pointDistanceThreshold() const { return m_pointDistanceThreshold; }
+ void setPointDistanceThreshold(qreal pointDistanceThreshold);
+
+signals:
+ void requiredPointCountChanged();
+ void pointDistanceThresholdChanged();
+
+protected:
+ struct PointData {
+ PointData() : id(0), angle(0) {}
+ PointData(quint64 id, qreal angle) : id(id), angle(angle) {}
+ quint64 id;
+ qreal angle;
+ };
+
+ bool wantsPointerEvent(QQuickPointerEvent *event) override;
+ bool sameAsCurrentPoints(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);
+ qreal averageStartingAngle(const QPointF &ref);
+ QVector<PointData> angles(const QPointF &ref) const;
+ static qreal averageAngleDelta(const QVector<PointData> &old, const QVector<PointData> &newAngles);
+ void acceptPoints(const QVector<QQuickEventPoint *> &points);
+ bool grabPoints(QVector<QQuickEventPoint *> points);
+
+protected:
+ QVector<QQuickEventPoint *> m_currentPoints;
+ int m_requiredPointCount;
+ qreal m_pointDistanceThreshold;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickMultiPointerHandler)
+
+#endif // QQUICKPOINTERMULTIHANDLER_H
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp
new file mode 100644
index 0000000000..465a49a6fd
--- /dev/null
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -0,0 +1,311 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "qquickpinchhandler_p.h"
+#include <QtQuick/qquickwindow.h>
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qquickitem_p.h>
+#include <private/qguiapplication_p.h>
+#include <private/qquickwindow_p.h>
+#include <QEvent>
+#include <QMouseEvent>
+#include <QDebug>
+#include <qpa/qplatformnativeinterface.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch")
+
+/*!
+ \qmltype PinchHandler
+ \instantiates QQuickPinchHandler
+ \inqmlmodule QtQuick
+ \ingroup qtquick-handlers
+ \brief Handler for pinch gestures
+
+ PinchHandler is a handler that is used to interactively rotate and zoom an Item.
+*/
+
+QQuickPinchHandler::QQuickPinchHandler(QObject *parent)
+ : QQuickMultiPointerHandler(parent, 2)
+ , m_activeScale(1)
+ , m_activeRotation(0)
+ , m_activeTranslation(0,0)
+ , m_minimumScale(-qInf())
+ , m_maximumScale(qInf())
+ , m_minimumRotation(-qInf())
+ , m_maximumRotation(qInf())
+ , m_minimumX(-qInf())
+ , m_maximumX(qInf())
+ , m_minimumY(-qInf())
+ , m_maximumY(qInf())
+ , m_pinchOrigin(PinchCenter)
+ , m_startScale(1)
+ , m_startRotation(0)
+{
+}
+
+QQuickPinchHandler::~QQuickPinchHandler()
+{
+}
+
+void QQuickPinchHandler::setMinimumScale(qreal minimumScale)
+{
+ if (m_minimumScale == minimumScale)
+ return;
+
+ m_minimumScale = minimumScale;
+ emit minimumScaleChanged();
+}
+
+void QQuickPinchHandler::setMaximumScale(qreal maximumScale)
+{
+ if (m_maximumScale == maximumScale)
+ return;
+
+ m_maximumScale = maximumScale;
+ emit maximumScaleChanged();
+}
+
+void QQuickPinchHandler::setMinimumRotation(qreal minimumRotation)
+{
+ if (m_minimumRotation == minimumRotation)
+ return;
+
+ m_minimumRotation = minimumRotation;
+ emit minimumRotationChanged();
+}
+
+void QQuickPinchHandler::setMaximumRotation(qreal maximumRotation)
+{
+ if (m_maximumRotation == maximumRotation)
+ return;
+
+ m_maximumRotation = maximumRotation;
+ emit maximumRotationChanged();
+}
+
+void QQuickPinchHandler::setPinchOrigin(QQuickPinchHandler::PinchOrigin pinchOrigin)
+{
+ if (m_pinchOrigin == pinchOrigin)
+ return;
+
+ m_pinchOrigin = pinchOrigin;
+ emit pinchOriginChanged();
+}
+
+/*!
+ \qmlproperty QQuickPinchHandler::minimumX
+
+ The minimum acceptable x coordinate of the centroid
+ */
+void QQuickPinchHandler::setMinimumX(qreal minX)
+{
+ if (m_minimumX == minX)
+ return;
+ m_minimumX = minX;
+ emit minimumXChanged();
+}
+
+/*!
+ \qmlproperty QQuickPinchHandler::maximumX
+
+ The maximum acceptable x coordinate of the centroid
+ */
+void QQuickPinchHandler::setMaximumX(qreal maxX)
+{
+ if (m_maximumX == maxX)
+ return;
+ m_maximumX = maxX;
+ emit maximumXChanged();
+}
+
+/*!
+ \qmlproperty QQuickPinchHandler::minimumY
+
+ The minimum acceptable y coordinate of the centroid
+ */
+void QQuickPinchHandler::setMinimumY(qreal minY)
+{
+ if (m_minimumY == minY)
+ return;
+ m_minimumY = minY;
+ emit minimumYChanged();
+}
+
+/*!
+ \qmlproperty QQuickPinchHandler::maximumY
+
+ The maximum acceptable y coordinate of the centroid
+ */
+void QQuickPinchHandler::setMaximumY(qreal maxY)
+{
+ if (m_maximumY == maxY)
+ return;
+ m_maximumY = maxY;
+ emit maximumYChanged();
+}
+
+/*!
+ \qmlproperty QQuickPinchHandler::minimumTouchPoints
+
+ The pinch begins when this number of fingers are pressed.
+ Until then, PinchHandler tracks the positions of any pressed fingers,
+ but if it's an insufficient number, it does not scale or rotate
+ its \l target, and the \l active property will remain false.
+*/
+
+/*!
+ \qmlproperty QQuickPinchHandler::active
+*/
+
+void QQuickPinchHandler::onActiveChanged()
+{
+ if (active()) {
+ if (const QQuickItem *t = target()) {
+ m_startScale = t->scale(); // TODO incompatible with independent x/y scaling
+ m_startRotation = t->rotation();
+ m_startCentroid = touchPointCentroid();
+ m_startAngles = angles(m_startCentroid);
+ m_startDistance = averageTouchPointDistance(m_startCentroid);
+ QVector3D xformOrigin(t->transformOriginPoint());
+ m_startMatrix = QMatrix4x4();
+ m_startMatrix.translate(t->x(), t->y());
+ m_startMatrix.translate(xformOrigin);
+ m_startMatrix.scale(m_startScale);
+ m_startMatrix.rotate(m_startRotation, 0, 0, -1);
+ m_startMatrix.translate(-xformOrigin);
+ m_activeRotation = 0;
+ m_activeTranslation = QPointF(0,0);
+ qCInfo(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation;
+ }
+ } else {
+ qCInfo(lcPinchHandler) << "deactivated with scale" << m_activeScale << "rotation" << m_activeRotation;
+ }
+}
+
+void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
+{
+ Q_UNUSED(event)
+ if (Q_UNLIKELY(lcPinchHandler().isDebugEnabled())) {
+ for (QQuickEventPoint *point : qAsConst(m_currentPoints))
+ qCDebug(lcPinchHandler) << point->state() << point->sceneGrabPos() << "->" << point->scenePos();
+ }
+
+ bool containsReleasedPoints = event->isReleaseEvent();
+ if (!active() && !containsReleasedPoints) {
+ // Verify that least one of the points have moved beyond threshold needed to activate the handler
+ for (QQuickEventPoint *point : qAsConst(m_currentPoints)) {
+ if (QQuickWindowPrivate::dragOverThreshold(point)) {
+ if (grabPoints(m_currentPoints))
+ setActive(true);
+ break;
+ }
+ }
+ if (!active())
+ return;
+ }
+ // TODO check m_pinchOrigin: right now it acts like it's set to PinchCenter
+ m_centroid = touchPointCentroid();
+ m_centroidVelocity = touchPointCentroidVelocity();
+ QRectF bounds(m_minimumX, m_minimumY, m_maximumX, m_maximumY);
+ // 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.
+ QPointF centroidParentPos;
+ if (target() && target()->parentItem()) {
+ centroidParentPos = target()->parentItem()->mapFromScene(m_centroid);
+ centroidParentPos = QPointF(qBound(bounds.left(), centroidParentPos.x(), bounds.right()),
+ qBound(bounds.top(), centroidParentPos.y(), bounds.bottom()));
+ }
+ // 1. scale
+ const qreal dist = averageTouchPointDistance(m_centroid);
+ m_activeScale = dist / m_startDistance;
+ m_activeScale = qBound(m_minimumScale/m_startScale, m_activeScale, m_maximumScale/m_startScale);
+ const qreal scale = m_startScale * m_activeScale;
+
+ // 2. rotate
+ QVector<PointData> newAngles = angles(m_centroid);
+ const qreal angleDelta = averageAngleDelta(m_startAngles, newAngles);
+ m_activeRotation += angleDelta;
+ 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
+ m_startAngles = std::move(newAngles);
+
+ if (target() && target()->parentItem()) {
+ // 3. Drag/translate
+ const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(m_startCentroid);
+ m_activeTranslation = centroidParentPos - centroidStartParentPos;
+
+ // apply rotation + scaling around the centroid - then apply translation.
+ QMatrix4x4 mat;
+
+ const QVector3D centroidParentVector(centroidParentPos);
+ mat.translate(centroidParentVector);
+ mat.rotate(m_activeRotation, 0, 0, 1);
+ mat.scale(m_activeScale);
+ mat.translate(-centroidParentVector);
+ mat.translate(QVector3D(m_activeTranslation));
+
+ mat = mat * m_startMatrix;
+
+ QPointF xformOriginPoint = target()->transformOriginPoint();
+ QPointF pos = mat * xformOriginPoint;
+ pos -= xformOriginPoint;
+
+ target()->setPosition(pos);
+ target()->setRotation(rotation);
+ target()->setScale(scale);
+
+
+ // TODO some translation inadvertently happens; try to hold the chosen pinch origin in place
+
+ qCDebug(lcPinchHandler) << "centroid" << m_startCentroid << "->" << m_centroid
+ << ", distance" << m_startDistance << "->" << dist
+ << ", startScale" << m_startScale << "->" << scale
+ << ", activeRotation" << m_activeRotation
+ << ", rotation" << rotation;
+ }
+
+ if (!containsReleasedPoints)
+ acceptPoints(m_currentPoints);
+ emit updated();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h
new file mode 100644
index 0000000000..6768196909
--- /dev/null
+++ b/src/quick/handlers/qquickpinchhandler_p.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** 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 QQUICKPINCHHANDLER_H
+#define QQUICKPINCHHANDLER_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 "qquickmultipointerhandler_p.h"
+#include <private/qquicktranslate_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickPinchHandler : public QQuickMultiPointerHandler
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal minimumScale READ minimumScale WRITE setMinimumScale NOTIFY minimumScaleChanged)
+ Q_PROPERTY(qreal maximumScale READ maximumScale WRITE setMaximumScale NOTIFY maximumScaleChanged)
+ 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 rotation READ rotation NOTIFY updated)
+ Q_PROPERTY(QPointF translation READ translation NOTIFY updated)
+ Q_PROPERTY(qreal minimumX READ minimumX WRITE setMinimumX NOTIFY minimumXChanged)
+ Q_PROPERTY(qreal maximumX READ maximumX WRITE setMaximumX NOTIFY maximumXChanged)
+ Q_PROPERTY(qreal minimumY READ minimumY WRITE setMinimumY NOTIFY minimumYChanged)
+ Q_PROPERTY(qreal maximumY READ maximumY WRITE setMaximumY NOTIFY maximumYChanged)
+
+public:
+ enum PinchOrigin {
+ FirstPoint, PinchCenter, TargetCenter
+ };
+ Q_ENUM(PinchOrigin)
+
+ explicit QQuickPinchHandler(QObject *parent = 0);
+ ~QQuickPinchHandler();
+
+ qreal minimumScale() const { return m_minimumScale; }
+ void setMinimumScale(qreal minimumScale);
+
+ qreal maximumScale() const { return m_maximumScale; }
+ void setMaximumScale(qreal maximumScale);
+
+ qreal minimumRotation() const { return m_minimumRotation; }
+ void setMinimumRotation(qreal minimumRotation);
+
+ qreal maximumRotation() const { return m_maximumRotation; }
+ void setMaximumRotation(qreal maximumRotation);
+
+ PinchOrigin pinchOrigin() const { return m_pinchOrigin; }
+ void setPinchOrigin(PinchOrigin pinchOrigin);
+
+ QPointF translation() const { return m_activeTranslation; }
+ qreal scale() 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; }
+ void setMaximumX(qreal maxX);
+ qreal minimumY() const { return m_minimumY; }
+ void setMinimumY(qreal minY);
+ qreal maximumY() const { return m_maximumY; }
+ void setMaximumY(qreal maxY);
+
+signals:
+ void requiredPointCountChanged();
+ void minimumScaleChanged();
+ void maximumScaleChanged();
+ void minimumRotationChanged();
+ void maximumRotationChanged();
+ void minimumXChanged();
+ void maximumXChanged();
+ void minimumYChanged();
+ void maximumYChanged();
+ void pinchOriginChanged();
+ void updated();
+
+protected:
+ void onActiveChanged() override;
+ void handlePointerEventImpl(QQuickPointerEvent *event) override;
+
+private:
+ // properties
+ qreal m_activeScale;
+ qreal m_activeRotation;
+ QPointF m_activeTranslation;
+ QPointF m_centroid;
+ QVector2D m_centroidVelocity;
+
+ qreal m_minimumScale;
+ qreal m_maximumScale;
+
+ qreal m_minimumRotation;
+ qreal m_maximumRotation;
+
+ qreal m_minimumX;
+ qreal m_maximumX;
+ qreal m_minimumY;
+ qreal m_maximumY;
+
+ PinchOrigin m_pinchOrigin;
+
+ // internal
+ qreal m_startScale;
+ qreal m_startRotation;
+ QPointF m_startCentroid;
+ qreal m_startDistance;
+ QPointF m_startPos;
+
+ QVector<PointData> m_startAngles;
+ QMatrix4x4 m_startMatrix;
+ QQuickMatrix4x4 m_transform;
+
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPinchHandler)
+
+#endif // QQUICKPINCHHANDLER_H
diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp
new file mode 100644
index 0000000000..dd0ff1f44c
--- /dev/null
+++ b/src/quick/handlers/qquickpointerdevicehandler.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "qquickpointerdevicehandler_p.h"
+#include <private/qquickitem_p.h>
+#include <QMouseEvent>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ An intermediate class (not registered as a QML type)
+ for handlers which allow filtering based on device type,
+ pointer type, or device-specific buttons (such as mouse or stylus buttons).
+ */
+QQuickPointerDeviceHandler::QQuickPointerDeviceHandler(QObject *parent)
+ : QQuickPointerHandler(parent)
+ , m_acceptedDevices(QQuickPointerDevice::AllDevices)
+ , m_acceptedPointerTypes(QQuickPointerDevice::AllPointerTypes)
+ , m_acceptedModifiers(Qt::KeyboardModifierMask)
+{
+}
+
+QQuickPointerDeviceHandler::~QQuickPointerDeviceHandler()
+{
+}
+
+void QQuickPointerDeviceHandler::setAcceptedDevices(QQuickPointerDevice::DeviceTypes acceptedDevices)
+{
+ if (m_acceptedDevices == acceptedDevices)
+ return;
+
+ m_acceptedDevices = acceptedDevices;
+ emit acceptedDevicesChanged();
+}
+
+void QQuickPointerDeviceHandler::setAcceptedPointerTypes(QQuickPointerDevice::PointerTypes acceptedPointerTypes)
+{
+ if (m_acceptedPointerTypes == acceptedPointerTypes)
+ return;
+
+ m_acceptedPointerTypes = acceptedPointerTypes;
+ emit acceptedPointerTypesChanged();
+}
+
+/*!
+ \qmlproperty int PointerDeviceHandler::acceptedModifiers
+
+ If this property is set, it will require the given keyboard modifiers to
+ be pressed in order to react to pointer events, and otherwise ignore them.
+
+ If this property is set to \c Qt.KeyboardModifierMask (the default value),
+ then the PointerHandler ignores the modifier keys.
+
+ For example, an \l [QML] Item could have two handlers of the same type,
+ one of which is enabled only if the required keyboard modifiers are
+ pressed:
+
+ \qml
+ Item {
+ TapHandler {
+ acceptedModifiers: Qt.ControlModifier
+ onTapped: console.log("control-tapped")
+ }
+ TapHandler {
+ acceptedModifiers: Qt.NoModifier
+ onTapped: console.log("tapped")
+ }
+ }
+ \endqml
+*/
+void QQuickPointerDeviceHandler::setAcceptedModifiers(Qt::KeyboardModifiers acceptedModifiers)
+{
+ if (m_acceptedModifiers == acceptedModifiers)
+ return;
+
+ m_acceptedModifiers = acceptedModifiers;
+ emit acceptedModifiersChanged();
+}
+
+bool QQuickPointerDeviceHandler::wantsPointerEvent(QQuickPointerEvent *event)
+{
+ 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)
+ return false;
+ if ((event->device()->pointerType() & m_acceptedPointerTypes) == 0)
+ return false;
+ if (m_acceptedModifiers != Qt::KeyboardModifierMask && event->modifiers() != m_acceptedModifiers)
+ return false;
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointerdevicehandler_p.h b/src/quick/handlers/qquickpointerdevicehandler_p.h
new file mode 100644
index 0000000000..9e30fa0be4
--- /dev/null
+++ b/src/quick/handlers/qquickpointerdevicehandler_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** 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 QQUICKPOINTERDEVICEHANDLER_H
+#define QQUICKPOINTERDEVICEHANDLER_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 "qquickpointerhandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+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::KeyboardModifiers acceptedModifiers READ acceptedModifiers WRITE setAcceptedModifiers NOTIFY acceptedModifiersChanged)
+
+public:
+ explicit QQuickPointerDeviceHandler(QObject *parent = 0);
+ ~QQuickPointerDeviceHandler();
+
+ QQuickPointerDevice::DeviceTypes acceptedDevices() const { return m_acceptedDevices; }
+ QQuickPointerDevice::PointerTypes acceptedPointerTypes() const { return m_acceptedPointerTypes; }
+ Qt::KeyboardModifiers acceptedModifiers() const { return m_acceptedModifiers; }
+
+public slots:
+ void setAcceptedDevices(QQuickPointerDevice::DeviceTypes acceptedDevices);
+ void setAcceptedPointerTypes(QQuickPointerDevice::PointerTypes acceptedPointerTypes);
+ void setAcceptedModifiers(Qt::KeyboardModifiers acceptedModifiers);
+
+Q_SIGNALS:
+ void acceptedDevicesChanged();
+ void acceptedPointerTypesChanged();
+ void acceptedModifiersChanged();
+
+protected:
+ bool wantsPointerEvent(QQuickPointerEvent *event) override;
+
+protected:
+ QQuickPointerDevice::DeviceTypes m_acceptedDevices;
+ QQuickPointerDevice::PointerTypes m_acceptedPointerTypes;
+ Qt::KeyboardModifiers m_acceptedModifiers;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPointerDeviceHandler)
+
+#endif // QQUICKPOINTERDEVICEHANDLER_H
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp
new file mode 100644
index 0000000000..bcccfac318
--- /dev/null
+++ b/src/quick/handlers/qquickpointerhandler.cpp
@@ -0,0 +1,255 @@
+/****************************************************************************
+**
+** 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 "qquickpointerhandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcPointerHandlerDispatch, "qt.quick.handler.dispatch")
+Q_LOGGING_CATEGORY(lcPointerHandlerActive, "qt.quick.handler.active")
+
+/*!
+ \qmltype PointerHandler
+ //! \instantiates QQuickPointerHandler
+ \inqmlmodule QtQuick
+ \ingroup qtquick-handlers
+ \brief Handler for pointer events.
+
+ PointerHandler is a handler for pointer events regardless of source.
+ They may represent events from a touch, mouse or tablet device.
+*/
+
+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)
+{
+}
+
+QQuickPointerHandler::~QQuickPointerHandler()
+{
+ QQuickItem *parItem = parentItem();
+ if (parItem) {
+ QQuickItemPrivate *p = QQuickItemPrivate::get(parItem);
+ p->extra.value().pointerHandlers.removeOne(this);
+ }
+}
+
+/*!
+ 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,
+ or null if the state change regards an Item. (TODO do we have any such cases?)
+ 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
+ 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)
+ whatever custom behavior it implements.
+*/
+void QQuickPointerHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point)
+{
+ qCDebug(lcPointerHandlerDispatch) << point << stateChange << grabber;
+ Q_ASSERT(point);
+ if (grabber == this) {
+ bool wasCanceled = false;
+ emit grabChanged(point);
+ switch (stateChange) {
+ case QQuickEventPoint::GrabPassive:
+ case QQuickEventPoint::GrabExclusive:
+ break;
+ case QQuickEventPoint::CancelGrabPassive:
+ case QQuickEventPoint::CancelGrabExclusive:
+ wasCanceled = true; // the grab was stolen by something else
+ Q_FALLTHROUGH();
+ case QQuickEventPoint::UngrabPassive:
+ case QQuickEventPoint::UngrabExclusive:
+ setActive(false);
+ point->setAccepted(false);
+ if (auto par = parentItem()) {
+ par->setKeepMouseGrab(m_hadKeepMouseGrab);
+ par->setKeepTouchGrab(m_hadKeepTouchGrab);
+ }
+ case QQuickEventPoint::OverrideGrabPassive:
+ // Passive grab is still there, but we won't receive point updates right now.
+ // No need to notify about this.
+ return;
+ }
+ if (wasCanceled)
+ emit canceled(point);
+ else
+ emit grabChanged(point);
+ }
+}
+
+void QQuickPointerHandler::setPassiveGrab(QQuickEventPoint *point, bool grab)
+{
+ qCDebug(lcPointerHandlerDispatch) << point << grab;
+ if (grab) {
+ point->setGrabberPointerHandler(this, false);
+ } else {
+ point->removePassiveGrabber(this);
+ }
+}
+
+void QQuickPointerHandler::setExclusiveGrab(QQuickEventPoint *point, bool grab)
+{
+ // TODO m_hadKeepMouseGrab m_hadKeepTouchGrab
+ qCDebug(lcPointerHandlerDispatch) << point << grab;
+ // Don't allow one handler to cancel another's grab, unless it is stealing it for itself
+ if (!grab && point->grabberPointerHandler() != this)
+ return;
+ point->setGrabberPointerHandler(grab ? this : nullptr, true);
+}
+
+void QQuickPointerHandler::cancelAllGrabs(QQuickEventPoint *point)
+{
+ qCDebug(lcPointerHandlerDispatch) << point;
+ point->cancelAllGrabs(this);
+}
+
+QPointF QQuickPointerHandler::eventPos(const QQuickEventPoint *point) const
+{
+ return (target() ? target()->mapFromScene(point->scenePos()) : point->scenePos());
+}
+
+bool QQuickPointerHandler::parentContains(const QQuickEventPoint *point) const
+{
+ if (point) {
+ if (QQuickItem *par = parentItem())
+ return par->contains(par->mapFromScene(point->scenePos()));
+ }
+ return false;
+}
+
+/*!
+ \qmlproperty bool PointerHandler::enabled
+
+ If a PointerHandler is disabled, it will reject all events
+ and no signals will be emitted.
+
+ TODO is it too extreme not even to emit pressed/updated/released?
+ or should we disable only the higher-level interpretation, in subclasses?
+*/
+void QQuickPointerHandler::setEnabled(bool enabled)
+{
+ if (m_enabled == enabled)
+ return;
+
+ m_enabled = enabled;
+ emit enabledChanged();
+}
+
+void QQuickPointerHandler::setTarget(QQuickItem *target)
+{
+ m_targetExplicitlySet = true;
+ if (m_target == target)
+ return;
+
+ m_target = target;
+ emit targetChanged();
+}
+
+QQuickItem *QQuickPointerHandler::target() const
+{
+ if (!m_targetExplicitlySet)
+ return parentItem();
+ return m_target;
+}
+
+void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event)
+{
+ bool wants = wantsPointerEvent(event);
+ qCDebug(lcPointerHandlerDispatch) << metaObject()->className() << objectName()
+ << "on" << parentItem()->metaObject()->className() << parentItem()->objectName()
+ << (wants ? "WANTS" : "DECLINES") << event;
+ if (wants) {
+ handlePointerEventImpl(event);
+ } else {
+ setActive(false);
+ int pCount = event->pointCount();
+ for (int i = 0; i < pCount; ++i) {
+ QQuickEventPoint *pt = event->point(i);
+ if (pt->grabberPointerHandler() == this && pt->state() != QQuickEventPoint::Stationary)
+ pt->cancelExclusiveGrab();
+ }
+ }
+ event->device()->eventDeliveryTargets().append(this);
+}
+
+bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event)
+{
+ Q_UNUSED(event)
+ return m_enabled;
+}
+
+void QQuickPointerHandler::setActive(bool active)
+{
+ if (m_active != active) {
+ qCDebug(lcPointerHandlerActive) << this << m_active << "->" << active;
+ m_active = active;
+ onActiveChanged();
+ emit activeChanged();
+ }
+}
+
+void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event)
+{
+ m_currentEvent = event;
+}
+
+/*!
+ \qmlproperty Item PointerHandler::parent
+
+ The \l Item which is the scope of the handler; the Item in which it was declared.
+ The handler will handle events on behalf of this Item, which means a
+ pointer event is relevant if at least one of its event points occurs within
+ the Item's interior. Initially \l target() is the same, but target()
+ can be reassigned.
+
+ \sa QQuickPointerHandler::target(), QObject::parent()
+*/
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h
new file mode 100644
index 0000000000..24a058275d
--- /dev/null
+++ b/src/quick/handlers/qquickpointerhandler_p.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** 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 QQUICKPOINTERHANDLER_H
+#define QQUICKPOINTERHANDLER_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/qquickitem_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcPointerHandlerDispatch)
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandler : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
+ Q_PROPERTY(bool active READ active NOTIFY activeChanged)
+ Q_PROPERTY(QQuickItem * target READ target WRITE setTarget NOTIFY targetChanged)
+ Q_PROPERTY(QQuickItem * parent READ parentItem CONSTANT)
+
+public:
+ explicit QQuickPointerHandler(QObject *parent = 0);
+ virtual ~QQuickPointerHandler();
+
+public:
+ bool enabled() const { return m_enabled; }
+ void setEnabled(bool enabled);
+
+ bool active() const { return m_active; }
+
+ QQuickItem *target() const;
+ void setTarget(QQuickItem *target);
+
+ QQuickItem * parentItem() const { return static_cast<QQuickItem *>(QObject::parent()); }
+
+ void handlePointerEvent(QQuickPointerEvent *event);
+
+Q_SIGNALS:
+ void enabledChanged();
+ void activeChanged();
+ void targetChanged();
+ void grabChanged(QQuickEventPoint *point);
+ void canceled(QQuickEventPoint *point);
+
+protected:
+ QQuickPointerEvent *currentEvent() { return m_currentEvent; }
+ virtual bool wantsPointerEvent(QQuickPointerEvent *event);
+ virtual void handlePointerEventImpl(QQuickPointerEvent *event);
+ void setActive(bool active);
+ virtual void onActiveChanged() { }
+ virtual void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point);
+ void setPassiveGrab(QQuickEventPoint *point, bool grab = true);
+ void setExclusiveGrab(QQuickEventPoint *point, bool grab = true);
+ void cancelAllGrabs(QQuickEventPoint *point);
+ 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
+
+ friend class QQuickEventPoint;
+ friend class QQuickWindowPrivate;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPointerHandler)
+
+#endif // QQUICKPOINTERHANDLER_H
diff --git a/src/quick/handlers/qquickpointersinglehandler.cpp b/src/quick/handlers/qquickpointersinglehandler.cpp
new file mode 100644
index 0000000000..0b0f482d8d
--- /dev/null
+++ b/src/quick/handlers/qquickpointersinglehandler.cpp
@@ -0,0 +1,243 @@
+/****************************************************************************
+**
+** 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 "qquickpointersinglehandler_p.h"
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH_TARGET)
+
+/*!
+ An intermediate class (not registered as a QML type)
+ for the most common handlers: those which expect only a single point.
+ wantsPointerEvent() will choose the first point which is inside the
+ \l target item, and return true as long as the event contains that point.
+ Override handleEventPoint() to implement a single-point handler.
+*/
+
+QQuickPointerSingleHandler::QQuickPointerSingleHandler(QObject *parent)
+ : QQuickPointerDeviceHandler(parent)
+ , m_acceptedButtons(Qt::LeftButton)
+ , m_ignoreAdditionalPoints(false)
+{
+}
+
+bool QQuickPointerSingleHandler::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)
+ return false;
+
+ if (m_pointInfo.m_id) {
+ // We already know which one we want, so check whether it's there.
+ // It's expected to be an update or a release.
+ // If we no longer want it, cancel the grab.
+ int candidatePointCount = 0;
+ QQuickEventPoint *point = nullptr;
+ int c = event->pointCount();
+ for (int i = 0; i < c; ++i) {
+ QQuickEventPoint *p = event->point(i);
+ if (wantsEventPoint(p)) {
+ ++candidatePointCount;
+ if (p->pointId() == m_pointInfo.m_id)
+ point = p;
+ }
+ }
+ if (point) {
+ if (candidatePointCount == 1 || (candidatePointCount > 1 && m_ignoreAdditionalPoints)) {
+ point->setAccepted();
+ return true;
+ } else {
+ 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 {
+ // We have not yet chosen a point; choose the first one for which wantsEventPoint() returns true.
+ int candidatePointCount = 0;
+ int c = event->pointCount();
+ QQuickEventPoint *chosen = nullptr;
+ for (int i = 0; i < c; ++i) {
+ QQuickEventPoint *p = event->point(i);
+ if (!p->exclusiveGrabber() && wantsEventPoint(p)) {
+ if (!chosen)
+ chosen = p;
+ ++candidatePointCount;
+ }
+ }
+ if (chosen && candidatePointCount == 1) {
+ m_pointInfo.m_id = chosen->pointId();
+ chosen->setAccepted();
+ }
+ }
+ return m_pointInfo.m_id;
+}
+
+void QQuickPointerSingleHandler::handlePointerEventImpl(QQuickPointerEvent *event)
+{
+ QQuickPointerDeviceHandler::handlePointerEventImpl(event);
+ QQuickEventPoint *currentPoint = event->pointById(m_pointInfo.m_id);
+ Q_ASSERT(currentPoint);
+ 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->pos();
+ m_pointInfo.m_scenePosition = currentPoint->scenePos();
+ if (currentPoint->state() == QQuickEventPoint::Updated)
+ m_pointInfo.m_velocity = currentPoint->velocity();
+ handleEventPoint(currentPoint);
+ switch (currentPoint->state()) {
+ case QQuickEventPoint::Pressed:
+ m_pointInfo.m_pressPosition = currentPoint->pos();
+ m_pointInfo.m_scenePressPosition = currentPoint->scenePos();
+ m_pointInfo.m_pressedButtons = event->buttons();
+ break;
+ case QQuickEventPoint::Released:
+ setExclusiveGrab(currentPoint, false);
+ reset();
+ break;
+ default:
+ m_pointInfo.m_pressedButtons = event->buttons();
+ break;
+ }
+ emit pointChanged();
+ }
+}
+
+bool QQuickPointerSingleHandler::wantsEventPoint(QQuickEventPoint *point)
+{
+ return parentContains(point);
+}
+
+void QQuickPointerSingleHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point)
+{
+ if (grabber != this)
+ return;
+ switch (stateChange) {
+ case QQuickEventPoint::GrabExclusive:
+ m_pointInfo.m_sceneGrabPosition = point->sceneGrabPos();
+ setActive(true);
+ QQuickPointerHandler::onGrabChanged(grabber, stateChange, point);
+ break;
+ case QQuickEventPoint::GrabPassive:
+ m_pointInfo.m_sceneGrabPosition = point->sceneGrabPos();
+ QQuickPointerHandler::onGrabChanged(grabber, stateChange, point);
+ break;
+ case QQuickEventPoint::OverrideGrabPassive:
+ return; // don't emit
+ case QQuickEventPoint::UngrabPassive:
+ case QQuickEventPoint::UngrabExclusive:
+ case QQuickEventPoint::CancelGrabPassive:
+ case QQuickEventPoint::CancelGrabExclusive:
+ // the grab is lost or relinquished, so the point is no longer relevant
+ QQuickPointerHandler::onGrabChanged(grabber, stateChange, point);
+ reset();
+ break;
+ }
+ emit singlePointGrabChanged();
+}
+
+void QQuickPointerSingleHandler::setIgnoreAdditionalPoints(bool v)
+{
+ m_ignoreAdditionalPoints = v;
+}
+
+void QQuickPointerSingleHandler::moveTarget(QPointF pos, QQuickEventPoint *point)
+{
+ target()->setPosition(pos);
+ m_pointInfo.m_scenePosition = point->scenePos();
+ m_pointInfo.m_position = target()->mapFromScene(m_pointInfo.m_scenePosition);
+}
+
+void QQuickPointerSingleHandler::setAcceptedButtons(Qt::MouseButtons buttons)
+{
+ if (m_acceptedButtons == buttons)
+ return;
+
+ m_acceptedButtons = buttons;
+ emit acceptedButtonsChanged();
+}
+
+void QQuickPointerSingleHandler::reset()
+{
+ setActive(false);
+ m_pointInfo.reset();
+}
+
+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;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointersinglehandler_p.h b/src/quick/handlers/qquickpointersinglehandler_p.h
new file mode 100644
index 0000000000..8898a1600b
--- /dev/null
+++ b/src/quick/handlers/qquickpointersinglehandler_p.h
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** 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 QQUICKPOINTERSINGLEHANDLER_H
+#define QQUICKPOINTERSINGLEHANDLER_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 QQuickPointerSingleHandler;
+
+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 QQuickPointerSingleHandler;
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerSingleHandler : 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 QQuickPointerSingleHandler(QObject *parent = 0);
+ virtual ~QQuickPointerSingleHandler() { }
+
+ 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;
+
+ QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_pointInfo.m_id); }
+ void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override;
+
+ void setIgnoreAdditionalPoints(bool v = true);
+
+ void moveTarget(QPointF pos, QQuickEventPoint *point);
+
+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(QQuickPointerSingleHandler)
+
+#endif // QQUICKPOINTERSINGLEHANDLER_H
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
new file mode 100644
index 0000000000..1bcf42a073
--- /dev/null
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -0,0 +1,360 @@
+/****************************************************************************
+**
+** 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 "qquicktaphandler_p.h"
+#include <qpa/qplatformtheme.h>
+#include <private/qguiapplication_p.h>
+#include <QtGui/qstylehints.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcTapHandler, "qt.quick.handler.tap")
+
+qreal QQuickTapHandler::m_multiTapInterval(0.0);
+// single tap distance is the same as the drag threshold
+int QQuickTapHandler::m_mouseMultiClickDistanceSquared(-1);
+int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1);
+
+/*!
+ \qmltype TapHandler
+ \instantiates QQuickTapHandler
+ \inqmlmodule QtQuick
+ \ingroup qtquick-handlers
+ \brief Handler for taps and clicks.
+
+ TapHandler is a handler for taps on a touchscreen or clicks on a mouse.
+
+ Detection of a valid tap gesture depends on \l gesturePolicy.
+ Note that buttons (such as QPushButton) are often implemented not to care
+ whether the press and release occur close together: if you press the button
+ and then change your mind, you need to drag all the way off the edge of the
+ button in order to cancel the click. Therefore the default
+ \l gesturePolicy is \c TapHandler.ReleaseWithinBounds. If you want to require
+ that the press and release are close together in both space and time,
+ set it to \c TapHandler.DragThreshold.
+
+ For multi-tap gestures (double-tap, triple-tap etc.), the distance moved
+ must not exceed QPlatformTheme::MouseDoubleClickDistance with mouse and
+ QPlatformTheme::TouchDoubleTapDistance with touch, and the time between
+ taps must not exceed QStyleHints::mouseDoubleClickInterval().
+
+ \sa MouseArea
+*/
+
+QQuickTapHandler::QQuickTapHandler(QObject *parent)
+ : QQuickPointerSingleHandler(parent)
+ , m_pressed(false)
+ , m_gesturePolicy(ReleaseWithinBounds)
+ , m_tapCount(0)
+ , m_longPressThreshold(-1)
+ , m_lastTapTimestamp(0.0)
+{
+ if (m_mouseMultiClickDistanceSquared < 0) {
+ m_multiTapInterval = qApp->styleHints()->mouseDoubleClickInterval() / 1000.0;
+ m_mouseMultiClickDistanceSquared = QGuiApplicationPrivate::platformTheme()->
+ themeHint(QPlatformTheme::MouseDoubleClickDistance).toInt();
+ m_mouseMultiClickDistanceSquared *= m_mouseMultiClickDistanceSquared;
+ m_touchMultiTapDistanceSquared = QGuiApplicationPrivate::platformTheme()->
+ themeHint(QPlatformTheme::TouchDoubleTapDistance).toInt();
+ m_touchMultiTapDistanceSquared *= m_touchMultiTapDistanceSquared;
+ }
+}
+
+QQuickTapHandler::~QQuickTapHandler()
+{
+}
+
+static bool dragOverThreshold(QQuickEventPoint *point)
+{
+ QPointF delta = point->scenePos() - point->scenePressPos();
+ return (QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point) ||
+ QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point));
+}
+
+bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point)
+{
+ // If the user has not violated any constraint, it could be a tap.
+ // Otherwise we want to give up the grab so that a competing handler
+ // (e.g. DragHandler) gets a chance to take over.
+ // Don't forget to emit released in case of a cancel.
+ bool ret = false;
+ switch (point->state()) {
+ case QQuickEventPoint::Pressed:
+ case QQuickEventPoint::Released:
+ ret = parentContains(point);
+ break;
+ case QQuickEventPoint::Updated:
+ switch (m_gesturePolicy) {
+ case DragThreshold:
+ ret = !dragOverThreshold(point);
+ break;
+ case WithinBounds:
+ ret = parentContains(point);
+ break;
+ case ReleaseWithinBounds:
+ ret = point->pointId() == this->point().id();
+ break;
+ }
+ break;
+ case QQuickEventPoint::Stationary:
+ // Never react in any way when the point hasn't moved.
+ // In autotests, the point's position may not even be correct, because
+ // QTest::touchEvent(window, touchDevice).stationary(1)
+ // provides no opportunity to give a position, so it ends up being random.
+ break;
+ }
+ // If this is the grabber, returning false from this function will cancel the grab,
+ // so onGrabChanged(this, CancelGrabExclusive, point) and setPressed(false) will be called.
+ // But when m_gesturePolicy is DragThreshold, we don't get an exclusive grab, but
+ // we still don't want to be pressed anymore.
+ if (!ret && point->pointId() == this->point().id() && point->state() != QQuickEventPoint::Stationary)
+ setPressed(false, true, point);
+ return ret;
+}
+
+void QQuickTapHandler::handleEventPoint(QQuickEventPoint *point)
+{
+ switch (point->state()) {
+ case QQuickEventPoint::Pressed:
+ setPressed(true, false, point);
+ break;
+ case QQuickEventPoint::Released:
+ if ((point->pointerEvent()->buttons() & acceptedButtons()) == Qt::NoButton)
+ setPressed(false, false, point);
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ \qmlproperty real TapHandler::longPressThreshold
+
+ The time in seconds that an event point must be pressed in order to
+ trigger a long press gesture and emit the \l longPressed() signal.
+ If the point is released before this time limit, a tap can be detected
+ if the \l gesturePolicy constraint is satisfied. The default value is
+ QStyleHints::mousePressAndHoldInterval() converted to seconds.
+*/
+qreal QQuickTapHandler::longPressThreshold() const
+{
+ return longPressThresholdMilliseconds() / 1000.0;
+}
+
+void QQuickTapHandler::setLongPressThreshold(qreal longPressThreshold)
+{
+ int ms = qRound(longPressThreshold * 1000);
+ if (m_longPressThreshold == ms)
+ return;
+
+ m_longPressThreshold = ms;
+ emit longPressThresholdChanged();
+}
+
+int QQuickTapHandler::longPressThresholdMilliseconds() const
+{
+ return (m_longPressThreshold < 0 ? QGuiApplication::styleHints()->mousePressAndHoldInterval() : m_longPressThreshold);
+}
+
+void QQuickTapHandler::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() == m_longPressTimer.timerId()) {
+ m_longPressTimer.stop();
+ qCDebug(lcTapHandler) << objectName() << "longPressed";
+ emit longPressed();
+ }
+}
+
+/*!
+ \qmlproperty enumeration TapHandler::gesturePolicy
+
+ The spatial constraint for a tap or long press gesture to be recognized,
+ in addition to the constraint that the release must occur before
+ \l longPressThreshold has elapsed. If these constraints are not satisfied,
+ the \l tapped signal is not emitted, and \l tapCount is not incremented.
+ If the spatial constraint is violated, \l isPressed transitions immediately
+ from true to false, regardless of the time held.
+
+ \value TapHandler.DragThreshold
+ The event point must not move significantly. If the mouse, finger
+ or stylus moves past the system-wide drag 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), because in this case TapHandler will
+ never grab.
+
+ \value TapHandler.WithinBounds
+ If the event point leaves the bounds of the \l target item, the tap
+ gesture is canceled. The TapHandler will grab on press, but release
+ the grab as soon as the boundary constraint is no longer satisfied.
+
+ \value TapHandler.ReleaseWithinBounds
+ (the default value) At the time of release (the mouse button is
+ released or the finger is lifted), if the event point is outside
+ the bounds of the \l target item, a tap gesture is not recognized.
+ This is the default value, because it corresponds to typical button
+ behavior: you can cancel a click by dragging outside the button,
+ and you can also change your mind by dragging back inside the button
+ before release. Note that it's necessary for TapHandler to grab on
+ press and retain it until release (greedy grab) in order to detect
+ this gesture.
+*/
+void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gesturePolicy)
+{
+ if (m_gesturePolicy == gesturePolicy)
+ return;
+
+ m_gesturePolicy = gesturePolicy;
+ emit gesturePolicyChanged();
+}
+
+/*!
+ \qmlproperty bool TapHandler::pressed
+ \readonly
+
+ Holds true whenever the mouse or touch point is pressed,
+ and any movement since the press is compliant with the current
+ \l gesturePolicy. When the event point is released or the policy is
+ violated, \e pressed will change to false.
+*/
+void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *point)
+{
+ if (m_pressed != press) {
+ qCDebug(lcTapHandler) << objectName() << "pressed" << m_pressed << "->" << press << (cancel ? "CANCEL" : "") << point;
+ m_pressed = press;
+ connectPreRenderSignal(press);
+ updateTimeHeld();
+ if (press) {
+ m_longPressTimer.start(longPressThresholdMilliseconds(), this);
+ m_holdTimer.start();
+ } else {
+ m_longPressTimer.stop();
+ m_holdTimer.invalidate();
+ }
+ if (press) {
+ // on press, grab before emitting changed signals
+ if (m_gesturePolicy == DragThreshold)
+ setPassiveGrab(point, press);
+ else
+ setExclusiveGrab(point, press);
+ }
+ if (!cancel && !press && parentContains(point)) {
+ if (point->timeHeld() < longPressThreshold()) {
+ // Assuming here that pointerEvent()->timestamp() is in ms.
+ qreal ts = point->pointerEvent()->timestamp() / 1000.0;
+ if (ts - m_lastTapTimestamp < m_multiTapInterval &&
+ QVector2D(point->scenePos() - m_lastTapPos).lengthSquared() <
+ (point->pointerEvent()->device()->type() == QQuickPointerDevice::Mouse ?
+ m_mouseMultiClickDistanceSquared : m_touchMultiTapDistanceSquared))
+ ++m_tapCount;
+ else
+ m_tapCount = 1;
+ qCDebug(lcTapHandler) << objectName() << "tapped" << m_tapCount << "times";
+ emit tapped(point);
+ emit tapCountChanged();
+ m_lastTapTimestamp = ts;
+ m_lastTapPos = point->scenePos();
+ } else {
+ qCDebug(lcTapHandler) << objectName() << "tap threshold" << longPressThreshold() << "exceeded:" << point->timeHeld();
+ }
+ }
+ emit pressedChanged();
+ if (!press && m_gesturePolicy != DragThreshold) {
+ // on release, ungrab after emitting changed signals
+ setExclusiveGrab(point, press);
+ }
+ }
+}
+
+void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point)
+{
+ QQuickPointerSingleHandler::onGrabChanged(grabber, stateChange, point);
+ bool isCanceled = stateChange == QQuickEventPoint::CancelGrabExclusive || stateChange == QQuickEventPoint::CancelGrabPassive;
+ if (grabber == this && (isCanceled || point->state() == QQuickEventPoint::Released))
+ setPressed(false, isCanceled, point);
+}
+
+void QQuickTapHandler::connectPreRenderSignal(bool conn)
+{
+ if (conn)
+ connect(parentItem()->window(), &QQuickWindow::beforeSynchronizing, this, &QQuickTapHandler::updateTimeHeld);
+ else
+ disconnect(parentItem()->window(), &QQuickWindow::beforeSynchronizing, this, &QQuickTapHandler::updateTimeHeld);
+}
+
+void QQuickTapHandler::updateTimeHeld()
+{
+ emit timeHeldChanged();
+}
+
+/*!
+ \qmlproperty int TapHandler::tapCount
+
+ The number of taps which have occurred within the time and space
+ constraints to be considered a single gesture. For example, to detect
+ a double-tap, you can write:
+
+ \qml
+ Rectangle {
+ width: 100; height: 30
+ signal doubleTap
+ TapHandler {
+ acceptedButtons: Qt.AllButtons
+ onTapped: if (tapCount == 2) doubleTap()
+ }
+ }
+ \endqml
+*/
+
+/*!
+ \qmlproperty real TapHandler::timeHeld
+
+ The amount of time in seconds that a pressed point has been held, without
+ moving beyond the drag threshold. It will be updated at least once per
+ frame rendered, which enables rendering an animation showing the progress
+ towards an action which will be triggered by a long-press. It is also
+ possible to trigger one of a series of actions depending on how long the
+ press is held.
+
+ A value of less than zero means no point is being held within this
+ handler's \l [QML] Item.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquicktaphandler_p.h b/src/quick/handlers/qquicktaphandler_p.h
new file mode 100644
index 0000000000..0e9a6f0411
--- /dev/null
+++ b/src/quick/handlers/qquicktaphandler_p.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** 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 QQUICKTAPHANDLER_H
+#define QQUICKTAPHANDLER_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 "qquickpointersinglehandler_p.h"
+#include <QtCore/qbasictimer.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickTapHandler : public QQuickPointerSingleHandler
+{
+ Q_OBJECT
+ Q_PROPERTY(bool isPressed READ isPressed NOTIFY pressedChanged)
+ Q_PROPERTY(int tapCount READ tapCount NOTIFY tapCountChanged)
+ Q_PROPERTY(qreal timeHeld READ timeHeld NOTIFY timeHeldChanged)
+ Q_PROPERTY(qreal longPressThreshold READ longPressThreshold WRITE setLongPressThreshold NOTIFY longPressThresholdChanged)
+ Q_PROPERTY(GesturePolicy gesturePolicy READ gesturePolicy WRITE setGesturePolicy NOTIFY gesturePolicyChanged)
+
+public:
+ enum GesturePolicy {
+ DragThreshold,
+ WithinBounds,
+ ReleaseWithinBounds
+ };
+ Q_ENUM(GesturePolicy)
+
+ explicit QQuickTapHandler(QObject *parent = 0);
+ ~QQuickTapHandler();
+
+ bool isPressed() const { return m_pressed; }
+
+ int tapCount() const { return m_tapCount; }
+ qreal timeHeld() const { return (m_holdTimer.isValid() ? m_holdTimer.elapsed() / 1000.0 : -1.0); }
+
+ qreal longPressThreshold() const;
+ void setLongPressThreshold(qreal longPressThreshold);
+
+ GesturePolicy gesturePolicy() const { return m_gesturePolicy; }
+ void setGesturePolicy(GesturePolicy gesturePolicy);
+
+Q_SIGNALS:
+ void pressedChanged();
+ void tapCountChanged();
+ void timeHeldChanged();
+ void longPressThresholdChanged();
+ void gesturePolicyChanged();
+ void tapped(QQuickEventPoint *point);
+ void longPressed();
+
+protected:
+ void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override;
+ void timerEvent(QTimerEvent *event) override;
+ bool wantsEventPoint(QQuickEventPoint *point) override;
+ void handleEventPoint(QQuickEventPoint *point) override;
+
+private:
+ void setPressed(bool press, bool cancel, QQuickEventPoint *point);
+ int longPressThresholdMilliseconds() const;
+ void connectPreRenderSignal(bool conn = true);
+ void updateTimeHeld();
+
+private:
+ bool m_pressed;
+ GesturePolicy m_gesturePolicy;
+ int m_tapCount;
+ int m_longPressThreshold;
+ QBasicTimer m_longPressTimer;
+ QElapsedTimer m_holdTimer;
+ QPointF m_lastTapPos;
+ qreal m_lastTapTimestamp;
+
+ static qreal m_multiTapInterval;
+ static int m_mouseMultiClickDistanceSquared;
+ static int m_touchMultiTapDistanceSquared;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickTapHandler)
+
+#endif // QQUICKTAPHANDLER_H
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index 5674326d6c..9ac7422a39 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -40,6 +40,7 @@
#include "qquickcontext2d_p.h"
#include "qquickcontext2dcommandbuffer_p.h"
#include "qquickcanvasitem_p.h"
+#include <private/qtquickglobal_p.h>
#include <private/qquickcontext2dtexture_p.h>
#include <private/qquickitem_p.h>
#if QT_CONFIG(quick_shadereffect)
@@ -129,8 +130,6 @@ QT_BEGIN_NAMESPACE
Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
-#define DEGREES(t) ((t) * 180.0 / M_PI)
-
#define CHECK_CONTEXT(r) if (!r || !r->d()->context || !r->d()->context->bufferValid()) \
THROW_GENERIC_ERROR("Not a Context2D object");
@@ -138,7 +137,7 @@ Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
THROW_GENERIC_ERROR("Not a Context2D object");
#define qClamp(val, min, max) qMin(qMax(val, min), max)
#define CHECK_RGBA(c) (c == '-' || c == '.' || (c >=0 && c <= 9))
-QColor qt_color_from_string(const QV4::Value &name)
+Q_QUICK_PRIVATE_EXPORT QColor qt_color_from_string(const QV4::Value &name)
{
QByteArray str = name.toQString().toUtf8();
@@ -905,7 +904,7 @@ struct QQuickJSContext2DPixelData : public QV4::Object
V4_NEEDS_DESTROY
static QV4::ReturnedValue getIndexed(const QV4::Managed *m, uint index, bool *hasProperty);
- static void putIndexed(QV4::Managed *m, uint index, const QV4::Value &value);
+ static bool putIndexed(QV4::Managed *m, uint index, const QV4::Value &value);
static void proto_get_length(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
};
@@ -929,9 +928,9 @@ struct QQuickJSContext2DImageData : public QV4::Object
static void method_get_height(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
static void method_get_data(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData);
- static void markObjects(QV4::Heap::Base *that, QV4::ExecutionEngine *engine) {
- static_cast<QQuickJSContext2DImageData::Data *>(that)->pixelData.mark(engine);
- QV4::Object::markObjects(that, engine);
+ static void markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack) {
+ static_cast<QQuickJSContext2DImageData::Data *>(that)->pixelData.mark(markStack);
+ QV4::Object::markObjects(that, markStack);
}
};
@@ -1643,7 +1642,7 @@ void QQuickJSContext2DPrototype::method_createConicalGradient(const QV4::Builtin
if (callData->argc >= 3) {
qreal x = callData->args[0].toNumber();
qreal y = callData->args[1].toNumber();
- qreal angle = DEGREES(callData->args[2].toNumber());
+ qreal angle = qRadiansToDegrees(callData->args[2].toNumber());
if (!qt_is_finite(x) || !qt_is_finite(y)) {
THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createConicalGradient(): Incorrect arguments");
}
@@ -3083,13 +3082,13 @@ QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(const QV4::Managed *m,
return QV4::Encode::undefined();
}
-void QQuickJSContext2DPixelData::putIndexed(QV4::Managed *m, uint index, const QV4::Value &value)
+bool QQuickJSContext2DPixelData::putIndexed(QV4::Managed *m, uint index, const QV4::Value &value)
{
Q_ASSERT(m->as<QQuickJSContext2DPixelData>());
QV4::ExecutionEngine *v4 = static_cast<QQuickJSContext2DPixelData *>(m)->engine();
QV4::Scope scope(v4);
if (scope.hasException())
- return;
+ return false;
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, static_cast<QQuickJSContext2DPixelData *>(m));
@@ -3115,7 +3114,10 @@ void QQuickJSContext2DPixelData::putIndexed(QV4::Managed *m, uint index, const Q
*pixel = qRgba(qRed(*pixel), qGreen(*pixel), qBlue(*pixel), v);
break;
}
+ return true;
}
+
+ return false;
}
/*!
\qmlmethod CanvasImageData QtQuick::Context2D::createImageData(real sw, real sh)
@@ -3367,7 +3369,7 @@ void QQuickContext2D::rotate(qreal angle)
return;
QTransform newTransform =state.matrix;
- newTransform.rotate(DEGREES(angle));
+ newTransform.rotate(qRadiansToDegrees(angle));
if (!newTransform.isInvertible()) {
state.invertibleCTM = false;
@@ -3376,7 +3378,7 @@ void QQuickContext2D::rotate(qreal angle)
state.matrix = newTransform;
buffer()->updateMatrix(state.matrix);
- m_path = QTransform().rotate(-DEGREES(angle)).map(m_path);
+ m_path = QTransform().rotate(-qRadiansToDegrees(angle)).map(m_path);
}
void QQuickContext2D::shear(qreal h, qreal v)
@@ -3773,8 +3775,8 @@ void QQuickContext2D::arc(qreal xc, qreal yc, qreal radius, qreal sar, qreal ear
antiClockWise = !antiClockWise;
//end hack
- float sa = DEGREES(sar);
- float ea = DEGREES(ear);
+ float sa = qRadiansToDegrees(sar);
+ float ea = qRadiansToDegrees(ear);
double span = 0;
diff --git a/src/quick/items/qquickanimatedimage_p.h b/src/quick/items/qquickanimatedimage_p.h
index 143fe8904d..54da093259 100644
--- a/src/quick/items/qquickanimatedimage_p.h
+++ b/src/quick/items/qquickanimatedimage_p.h
@@ -97,7 +97,6 @@ Q_SIGNALS:
void playingChanged();
void pausedChanged();
void frameChanged();
- void sourceSizeChanged();
private Q_SLOTS:
void movieUpdate();
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index 448b63c347..1e0d268f93 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -38,8 +38,10 @@
****************************************************************************/
#include "qquickevents_p_p.h"
+#include <QtCore/qmap.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickpointerhandler_p.h>
#include <QtQuick/private/qquickwindow_p.h>
#include <private/qdebug_p.h>
@@ -448,7 +450,15 @@ Item {
typedef QHash<QTouchDevice *, QQuickPointerDevice *> PointerDeviceForTouchDeviceHash;
Q_GLOBAL_STATIC(PointerDeviceForTouchDeviceHash, g_touchDevices)
-Q_GLOBAL_STATIC_WITH_ARGS(QQuickPointerDevice, g_genericMouseDevice,
+struct ConstructableQQuickPointerDevice : public QQuickPointerDevice
+{
+ ConstructableQQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps,
+ int maxPoints, int buttonCount, const QString &name,
+ qint64 uniqueId = 0)
+ : QQuickPointerDevice(devType, pType, caps, maxPoints, buttonCount, name, uniqueId) {}
+
+};
+Q_GLOBAL_STATIC_WITH_ARGS(ConstructableQQuickPointerDevice, g_genericMouseDevice,
(QQuickPointerDevice::Mouse,
QQuickPointerDevice::GenericPointer,
QQuickPointerDevice::Position | QQuickPointerDevice::Scroll | QQuickPointerDevice::Hover,
@@ -457,6 +467,22 @@ Q_GLOBAL_STATIC_WITH_ARGS(QQuickPointerDevice, g_genericMouseDevice,
typedef QHash<qint64, QQuickPointerDevice *> PointerDeviceForDeviceIdHash;
Q_GLOBAL_STATIC(PointerDeviceForDeviceIdHash, g_tabletDevices)
+// debugging helpers
+static const char *pointStateString(const QQuickEventPoint *point)
+{
+ static const QMetaEnum stateMetaEnum = point->metaObject()->enumerator(point->metaObject()->indexOfEnumerator("State"));
+ return stateMetaEnum.valueToKey(point->state());
+}
+
+static const QString pointDeviceName(const QQuickEventPoint *point)
+{
+ auto device = static_cast<const QQuickPointerEvent *>(point->parent())->device();
+ QString deviceName = (device ? device->name() : QLatin1String("null device"));
+ deviceName.resize(16, ' '); // shorten, and align in case of sequential output
+ return deviceName;
+}
+
+
QQuickPointerDevice *QQuickPointerDevice::touchDevice(QTouchDevice *d)
{
if (g_touchDevices->contains(d))
@@ -505,37 +531,241 @@ QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id)
return nullptr;
}
-void QQuickEventPoint::reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp)
+void QQuickEventPoint::reset(Qt::TouchPointState state, const QPointF &scenePos, int pointId, ulong timestamp, const QVector2D &velocity)
{
m_scenePos = scenePos;
m_pointId = pointId;
- m_valid = true;
m_accept = false;
m_state = static_cast<QQuickEventPoint::State>(state);
m_timestamp = timestamp;
- if (state == Qt::TouchPointPressed)
+ if (state == Qt::TouchPointPressed) {
m_pressTimestamp = timestamp;
- // TODO calculate velocity
+ m_scenePressPos = scenePos;
+ }
+ m_velocity = (Q_LIKELY(velocity.isNull()) ? estimatedVelocity() : velocity);
+}
+
+void QQuickEventPoint::localizePosition(QQuickItem *target)
+{
+ if (target)
+ m_pos = target->mapFromScene(scenePos());
+ else
+ m_pos = QPointF();
+}
+
+/*!
+ If this point has an exclusive grabber, returns a pointer to it; else
+ returns null, if there is no grabber. The grabber could be either
+ an Item or a PointerHandler.
+*/
+QObject *QQuickEventPoint::exclusiveGrabber() const
+{
+ return m_exclusiveGrabber.data();
+}
+
+/*!
+ Set the given Item or PointerHandler as the exclusive grabber of this point.
+ If there was already an exclusive grab, it will be canceled. If there
+ were passive grabbers, they will continue to lurk, but the exclusive grab
+ is a behavioral override of the passive grab as long as it remains.
+ If you already know whether the grabber is to be an Item or a PointerHandler,
+ you should instead call setGrabberItem() or setGrabberPointerHandler(),
+ because it is slightly more efficient.
+*/
+void QQuickEventPoint::setExclusiveGrabber(QObject *grabber)
+{
+ if (QQuickPointerHandler *phGrabber = qmlobject_cast<QQuickPointerHandler *>(grabber))
+ setGrabberPointerHandler(phGrabber, true);
+ else
+ setGrabberItem(static_cast<QQuickItem *>(grabber));
+}
+
+/*!
+ If the exclusive grabber of this point is an Item, returns a
+ pointer to that Item; else returns null, if there is no grabber or if
+ the grabber is a PointerHandler.
+*/
+QQuickItem *QQuickEventPoint::grabberItem() const
+{
+ return (m_grabberIsHandler ? nullptr : static_cast<QQuickItem *>(m_exclusiveGrabber.data()));
+}
+
+/*!
+ Set the given Item \a grabber as the exclusive grabber of this point.
+ If there was already an exclusive grab, it will be canceled. If there
+ were passive grabbers, they will continue to lurk, but the exclusive grab
+ is a behavioral override of the passive grab as long as it remains.
+*/
+void QQuickEventPoint::setGrabberItem(QQuickItem *grabber)
+{
+ if (grabber != m_exclusiveGrabber.data()) {
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
+ qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this)
+ << ": grab" << m_exclusiveGrabber << "->" << grabber;
+ }
+ QQuickPointerHandler *oldGrabberHandler = grabberPointerHandler();
+ QQuickItem *oldGrabberItem = grabberItem();
+ m_exclusiveGrabber = QPointer<QObject>(grabber);
+ m_grabberIsHandler = false;
+ m_sceneGrabPos = m_scenePos;
+ if (oldGrabberHandler)
+ oldGrabberHandler->onGrabChanged(oldGrabberHandler, CancelGrabExclusive, this);
+ else if (oldGrabberItem && oldGrabberItem != grabber && grabber && pointerEvent()->asPointerTouchEvent())
+ oldGrabberItem->touchUngrabEvent();
+ for (QPointer<QQuickPointerHandler> passiveGrabber : m_passiveGrabbers)
+ passiveGrabber->onGrabChanged(passiveGrabber, OverrideGrabPassive, this);
+ }
+}
+
+/*!
+ If the exclusive grabber of this point is a PointerHandler, returns a
+ pointer to that handler; else returns null, if there is no grabber or if
+ the grabber is an Item.
+*/
+QQuickPointerHandler *QQuickEventPoint::grabberPointerHandler() const
+{
+ return (m_grabberIsHandler ? static_cast<QQuickPointerHandler *>(m_exclusiveGrabber.data()) : nullptr);
+}
+
+/*!
+ Set the given PointerHandler \a grabber as grabber of this point. If \a
+ exclusive is true, it will override any other grabs; if false, \a grabber
+ will be added to the list of passive grabbers of this point.
+*/
+void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, bool exclusive)
+{
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
+ if (exclusive) {
+ if (m_exclusiveGrabber != grabber)
+ qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this)
+ << ": grab (exclusive)" << m_exclusiveGrabber << "->" << grabber;
+ } else {
+ qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this)
+ << ": grab (passive)" << grabber;
+ }
+ }
+ if (exclusive) {
+ if (grabber != m_exclusiveGrabber.data()) {
+ if (grabber) {
+ // set variables before notifying the new grabber
+ m_exclusiveGrabber = QPointer<QObject>(grabber);
+ m_grabberIsHandler = true;
+ m_sceneGrabPos = m_scenePos;
+ grabber->onGrabChanged(grabber, GrabExclusive, this);
+ for (QPointer<QQuickPointerHandler> passiveGrabber : m_passiveGrabbers) {
+ if (passiveGrabber != grabber)
+ passiveGrabber->onGrabChanged(grabber, OverrideGrabPassive, this);
+ }
+ } else if (QQuickPointerHandler *oldGrabberPointerHandler = qmlobject_cast<QQuickPointerHandler *>(m_exclusiveGrabber.data())) {
+ oldGrabberPointerHandler->onGrabChanged(oldGrabberPointerHandler, UngrabExclusive, this);
+ } else if (!m_exclusiveGrabber.isNull()) {
+ // If there is a previous grabber and it's not a PointerHandler, it must be an Item.
+ QQuickItem *oldGrabberItem = static_cast<QQuickItem *>(m_exclusiveGrabber.data());
+ // If this point came from a touchscreen, notify that previous grabber Item that it's losing its touch grab.
+ if (pointerEvent()->asPointerTouchEvent())
+ oldGrabberItem->touchUngrabEvent();
+ }
+ // set variables after notifying the old grabber
+ m_exclusiveGrabber = QPointer<QObject>(grabber);
+ m_grabberIsHandler = true;
+ m_sceneGrabPos = m_scenePos;
+ }
+ } else {
+ if (!grabber) {
+ qDebug() << "can't set passive grabber to null";
+ return;
+ }
+ auto ptr = QPointer<QQuickPointerHandler>(grabber);
+ if (!m_passiveGrabbers.contains(ptr)) {
+ m_passiveGrabbers.append(ptr);
+ grabber->onGrabChanged(grabber, GrabPassive, this);
+ }
+ }
+}
+
+/*!
+ If this point has an existing exclusive grabber (Item or PointerHandler),
+ inform the grabber that its grab is canceled, and remove it as grabber.
+ This normally happens when the grab is stolen by another Item.
+*/
+void QQuickEventPoint::cancelExclusiveGrab()
+{
+ if (m_exclusiveGrabber.isNull())
+ qWarning("cancelGrab: no grabber");
+ else
+ cancelExclusiveGrabImpl();
+}
+
+void QQuickEventPoint::cancelExclusiveGrabImpl(QTouchEvent *cancelEvent)
+{
+ if (m_exclusiveGrabber.isNull())
+ return;
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
+ qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this)
+ << ": grab (exclusive)" << m_exclusiveGrabber << "-> nullptr";
+ }
+ if (auto handler = grabberPointerHandler()) {
+ handler->onGrabChanged(handler, CancelGrabExclusive, this);
+ } else if (auto item = grabberItem()) {
+ if (cancelEvent)
+ QCoreApplication::sendEvent(item, cancelEvent);
+ else
+ item->touchUngrabEvent();
+ }
+ m_exclusiveGrabber.clear();
+}
+
+/*!
+ If this point has the given \a handler as a passive grabber,
+ inform the grabber that its grab is canceled, and remove it as grabber.
+ This normally happens when another Item or PointerHandler does an exclusive grab.
+*/
+void QQuickEventPoint::cancelPassiveGrab(QQuickPointerHandler *handler)
+{
+ if (removePassiveGrabber(handler)) {
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled())) {
+ qCDebug(lcPointerGrab) << pointDeviceName(this) << "point" << hex << m_pointId << pointStateString(this)
+ << ": grab (passive)" << handler << "removed";
+ }
+ handler->onGrabChanged(handler, CancelGrabPassive, this);
+ }
}
-QQuickItem *QQuickEventPoint::grabber() const
+/*!
+ If this point has the given \a handler as a passive grabber, remove it as grabber.
+ Returns true if it was removed, false if it wasn't a grabber.
+*/
+bool QQuickEventPoint::removePassiveGrabber(QQuickPointerHandler *handler)
{
- return m_grabber.data();
+ return m_passiveGrabbers.removeOne(handler);
}
-void QQuickEventPoint::setGrabber(QQuickItem *grabber)
+/*!
+ If the given \a handler is grabbing this point passively, exclusively
+ or both, cancel the grab and remove it as grabber.
+ This normally happens when the handler decides that the behavior of this
+ point can no longer satisfy the handler's behavioral constraints within
+ the remainder of the gesture which the user is performing: for example
+ the handler tries to detect a tap but a drag is occurring instead, or
+ it tries to detect a drag in one direction but the drag is going in
+ another direction. In such cases the handler no longer needs or wants
+ to be informed of any further movements of this point.
+*/
+void QQuickEventPoint::cancelAllGrabs(QQuickPointerHandler *handler)
{
- if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled()) && m_grabber.data() != grabber) {
- auto device = static_cast<const QQuickPointerEvent *>(parent())->device();
- static const QMetaEnum stateMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("State"));
- QString deviceName = (device ? device->name() : QLatin1String("null device"));
- deviceName.resize(16, ' '); // shorten, and align in case of sequential output
- qCDebug(lcPointerGrab) << deviceName << "point" << hex << m_pointId << stateMetaEnum.valueToKey(state())
- << ": grab" << m_grabber << "->" << grabber;
+ if (m_exclusiveGrabber == handler) {
+ handler->onGrabChanged(handler, CancelGrabExclusive, this);
+ m_exclusiveGrabber.clear();
}
- m_grabber = QPointer<QQuickItem>(grabber);
+ cancelPassiveGrab(handler);
}
+/*!
+ Set this point as \a accepted (true) or rejected (false).
+ Accepting a point is intended to stop event propagation.
+ It does not imply any kind of grab, passive or exclusive.
+ TODO explain further under what conditions propagation really does stop...
+*/
void QQuickEventPoint::setAccepted(bool accepted)
{
if (m_accept != accepted) {
@@ -550,12 +780,74 @@ QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent)
void QQuickEventTouchPoint::reset(const QTouchEvent::TouchPoint &tp, ulong timestamp)
{
- QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id(), timestamp);
+ QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id(), timestamp, tp.velocity());
+ m_exclusiveGrabber.clear();
+ m_passiveGrabbers.clear();
m_rotation = tp.rotation();
m_pressure = tp.pressure();
+ m_ellipseDiameters = tp.ellipseDiameters();
m_uniqueId = tp.uniqueId();
}
+struct PointVelocityData {
+ QVector2D velocity;
+ QPointF pos;
+ ulong timestamp;
+};
+
+typedef QMap<quint64, PointVelocityData*> PointDataForPointIdMap;
+Q_GLOBAL_STATIC(PointDataForPointIdMap, g_previousPointData)
+static const int PointVelocityAgeLimit = 500; // milliseconds
+
+/*!
+ \internal
+ Estimates the velocity based on a weighted average of all previous velocities.
+ The older the velocity is, the less significant it becomes for the estimate.
+*/
+QVector2D QQuickEventPoint::estimatedVelocity() const
+{
+ PointVelocityData *prevPoint = g_previousPointData->value(m_pointId);
+ if (!prevPoint) {
+ // cleanup events older than PointVelocityAgeLimit
+ auto end = g_previousPointData->end();
+ for (auto it = g_previousPointData->begin(); it != end; ) {
+ PointVelocityData *data = it.value();
+ if (m_timestamp - data->timestamp > PointVelocityAgeLimit) {
+ it = g_previousPointData->erase(it);
+ delete data;
+ } else {
+ ++it;
+ }
+ }
+ // TODO optimize: stop this dynamic memory thrashing
+ prevPoint = new PointVelocityData;
+ prevPoint->velocity = QVector2D();
+ prevPoint->timestamp = 0;
+ prevPoint->pos = QPointF();
+ g_previousPointData->insert(m_pointId, prevPoint);
+ }
+ if (prevPoint) {
+ const ulong timeElapsed = m_timestamp - prevPoint->timestamp;
+ if (timeElapsed == 0) // in case we call estimatedVelocity() twice on the same QQuickEventPoint
+ return m_velocity;
+
+ QVector2D newVelocity;
+ if (prevPoint->timestamp != 0)
+ newVelocity = QVector2D(m_scenePos - prevPoint->pos)/timeElapsed;
+
+ // VERY simple kalman filter: does a weighted average
+ // where the older velocities get less and less significant
+ static const float KalmanGain = 0.7f;
+ QVector2D filteredVelocity = newVelocity * KalmanGain + m_velocity * (1.0f - KalmanGain);
+
+ prevPoint->velocity = filteredVelocity;
+ prevPoint->pos = m_scenePos;
+ prevPoint->timestamp = m_timestamp;
+ return filteredVelocity;
+ }
+ return QVector2D();
+}
+
/*!
\internal
\class QQuickPointerEvent
@@ -581,11 +873,14 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event)
return this;
m_device = QQuickPointerDevice::genericMouseDevice();
+ m_device->eventDeliveryTargets().clear();
m_button = ev->button();
m_pressedButtons = ev->buttons();
Qt::TouchPointState state = Qt::TouchPointStationary;
switch (ev->type()) {
case QEvent::MouseButtonPress:
+ m_mousePoint->clearPassiveGrabbers();
+ Q_FALLTHROUGH();
case QEvent::MouseButtonDblClick:
state = Qt::TouchPointPressed;
break;
@@ -598,10 +893,15 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event)
default:
break;
}
- m_mousePoint->reset(state, ev->windowPos(), 0, ev->timestamp()); // mouse is 0
+ m_mousePoint->reset(state, ev->windowPos(), quint64(1) << 24, ev->timestamp()); // mouse has device ID 1
return this;
}
+void QQuickPointerMouseEvent::localize(QQuickItem *target)
+{
+ m_mousePoint->localizePosition(target);
+}
+
QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event)
{
auto ev = static_cast<QTouchEvent*>(event);
@@ -610,6 +910,7 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event)
return this;
m_device = QQuickPointerDevice::touchDevice(ev->device());
+ m_device->eventDeliveryTargets().clear();
m_button = Qt::NoButton;
m_pressedButtons = Qt::NoButton;
@@ -620,32 +921,63 @@ QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event)
for (int i = m_touchPoints.size(); i < newPointCount; ++i)
m_touchPoints.insert(i, new QQuickEventTouchPoint(this));
- // Make sure the grabbers are right from one event to the next
- QVector<QQuickItem*> grabbers;
- // Copy all grabbers, because the order of points might have changed in the event.
+ // Make sure the grabbers and on-pressed values are right from one event to the next
+ struct ToPreserve {
+ int pointId; // just for double-checking
+ ulong pressTimestamp;
+ QPointF scenePressPos;
+ QPointF sceneGrabPos;
+ QObject * grabber;
+ QVector <QPointer <QQuickPointerHandler> > passiveGrabbers;
+
+ ToPreserve() : pointId(0), pressTimestamp(0), grabber(nullptr) {}
+ };
+ QVector<ToPreserve> preserves(newPointCount); // jar of pickled touchpoints, in order of points in the _new_ event
+
+ // Copy stuff we need to preserve, because the order of points might have changed in the event.
// The ID is all that we can rely on (release might remove the first point etc).
for (int i = 0; i < newPointCount; ++i) {
- QQuickItem *grabber = nullptr;
- if (auto point = pointById(tps.at(i).id()))
- grabber = point->grabber();
- grabbers.append(grabber);
+ int pid = tps.at(i).id();
+ if (auto point = pointById(pid)) {
+ preserves[i].pointId = pid;
+ preserves[i].pressTimestamp = point->m_pressTimestamp;
+ preserves[i].scenePressPos = point->scenePressPos();
+ preserves[i].sceneGrabPos = point->sceneGrabPos();
+ preserves[i].grabber = point->exclusiveGrabber();
+ preserves[i].passiveGrabbers = point->passiveGrabbers();
+ }
}
for (int i = 0; i < newPointCount; ++i) {
auto point = m_touchPoints.at(i);
point->reset(tps.at(i), ev->timestamp());
+ const auto &preserved = preserves.at(i);
if (point->state() == QQuickEventPoint::Pressed) {
- if (grabbers.at(i))
+ if (preserved.grabber)
qWarning() << "TouchPointPressed without previous release event" << point;
- point->setGrabber(nullptr);
+ point->setGrabberItem(nullptr);
+ point->clearPassiveGrabbers();
} else {
- point->setGrabber(grabbers.at(i));
+ // Restore the grabbers without notifying (don't call onGrabChanged)
+ Q_ASSERT(preserved.pointId == 0 || preserved.pointId == point->pointId());
+ point->m_pressTimestamp = preserved.pressTimestamp;
+ point->m_scenePressPos = preserved.scenePressPos;
+ point->m_sceneGrabPos = preserved.sceneGrabPos;
+ point->m_exclusiveGrabber = preserved.grabber;
+ point->m_grabberIsHandler = (qmlobject_cast<QQuickPointerHandler *>(point->m_exclusiveGrabber) != nullptr);
+ point->m_passiveGrabbers = preserved.passiveGrabbers;
}
}
m_pointCount = newPointCount;
return this;
}
+void QQuickPointerTouchEvent::localize(QQuickItem *target)
+{
+ for (auto point : qAsConst(m_touchPoints))
+ point->localizePosition(target);
+}
+
QQuickEventPoint *QQuickPointerMouseEvent::point(int i) const {
if (i == 0)
return m_mousePoint;
@@ -659,8 +991,8 @@ QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const {
}
QQuickEventPoint::QQuickEventPoint(QQuickPointerEvent *parent)
- : QObject(parent), m_pointId(0), m_grabber(nullptr), m_timestamp(0), m_pressTimestamp(0),
- m_state(QQuickEventPoint::Released), m_valid(false), m_accept(false)
+ : QObject(parent), m_pointId(0), m_exclusiveGrabber(nullptr), m_timestamp(0), m_pressTimestamp(0),
+ m_state(QQuickEventPoint::Released), m_accept(false), m_grabberIsHandler(false)
{
Q_UNUSED(m_reserved);
}
@@ -674,6 +1006,15 @@ bool QQuickPointerMouseEvent::allPointsAccepted() const {
return m_mousePoint->isAccepted();
}
+bool QQuickPointerMouseEvent::allUpdatedPointsAccepted() const {
+ return m_mousePoint->state() == QQuickEventPoint::Pressed || m_mousePoint->isAccepted();
+}
+
+bool QQuickPointerMouseEvent::allPointsGrabbed() const
+{
+ return m_mousePoint->exclusiveGrabber() != nullptr;
+}
+
QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) const
{
auto event = static_cast<QMouseEvent *>(m_event);
@@ -681,16 +1022,31 @@ QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) cons
return event;
}
-QVector<QQuickItem *> QQuickPointerMouseEvent::grabbers() const
+/*!
+ Returns the exclusive grabber of this event, if any, in a vector.
+*/
+QVector<QObject *> QQuickPointerMouseEvent::exclusiveGrabbers() const
{
- QVector<QQuickItem *> result;
- if (QQuickItem *grabber = m_mousePoint->grabber())
+ QVector<QObject *> result;
+ if (QObject *grabber = m_mousePoint->exclusiveGrabber())
result << grabber;
return result;
}
+/*!
+ Remove all passive and exclusive grabbers of this event, without notifying.
+*/
void QQuickPointerMouseEvent::clearGrabbers() const {
- m_mousePoint->setGrabber(nullptr);
+ m_mousePoint->setGrabberItem(nullptr);
+ m_mousePoint->clearPassiveGrabbers();
+}
+
+/*!
+ Returns whether the given \a handler is the exclusive grabber of this event.
+*/
+bool QQuickPointerMouseEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
+{
+ return m_mousePoint->exclusiveGrabber() == handler;
}
bool QQuickPointerMouseEvent::isPressEvent() const
@@ -700,6 +1056,24 @@ bool QQuickPointerMouseEvent::isPressEvent() const
(me->buttons() & me->button()) == me->buttons());
}
+bool QQuickPointerMouseEvent::isDoubleClickEvent() const
+{
+ auto me = static_cast<QMouseEvent*>(m_event);
+ return (me->type() == QEvent::MouseButtonDblClick);
+}
+
+bool QQuickPointerMouseEvent::isUpdateEvent() const
+{
+ auto me = static_cast<QMouseEvent*>(m_event);
+ return me->type() == QEvent::MouseMove;
+}
+
+bool QQuickPointerMouseEvent::isReleaseEvent() const
+{
+ auto me = static_cast<QMouseEvent*>(m_event);
+ return me->type() == QEvent::MouseButtonRelease;
+}
+
bool QQuickPointerTouchEvent::allPointsAccepted() const {
for (int i = 0; i < m_pointCount; ++i) {
if (!m_touchPoints.at(i)->isAccepted())
@@ -708,12 +1082,32 @@ bool QQuickPointerTouchEvent::allPointsAccepted() const {
return true;
}
-QVector<QQuickItem *> QQuickPointerTouchEvent::grabbers() const
-{
- QVector<QQuickItem *> result;
+bool QQuickPointerTouchEvent::allUpdatedPointsAccepted() const {
for (int i = 0; i < m_pointCount; ++i) {
auto point = m_touchPoints.at(i);
- if (QQuickItem *grabber = point->grabber()) {
+ if (point->state() != QQuickEventPoint::Pressed && !point->isAccepted())
+ return false;
+ }
+ return true;
+}
+
+bool QQuickPointerTouchEvent::allPointsGrabbed() const
+{
+ for (int i = 0; i < m_pointCount; ++i) {
+ if (!m_touchPoints.at(i)->exclusiveGrabber())
+ return false;
+ }
+ return true;
+}
+
+/*!
+ Returns the exclusive grabbers of all points in this event, if any, in a vector.
+*/
+QVector<QObject *> QQuickPointerTouchEvent::exclusiveGrabbers() const
+{
+ QVector<QObject *> result;
+ for (int i = 0; i < m_pointCount; ++i) {
+ if (QObject *grabber = m_touchPoints.at(i)->exclusiveGrabber()) {
if (!result.contains(grabber))
result << grabber;
}
@@ -721,9 +1115,27 @@ QVector<QQuickItem *> QQuickPointerTouchEvent::grabbers() const
return result;
}
+/*!
+ Remove all passive and exclusive grabbers of all touchpoints in this event,
+ without notifying.
+*/
void QQuickPointerTouchEvent::clearGrabbers() const {
+ for (auto point: m_touchPoints) {
+ point->setGrabberItem(nullptr);
+ point->clearPassiveGrabbers();
+ }
+}
+
+/*!
+ Returns whether the given \a handler is the exclusive grabber of any
+ touchpoint within this event.
+*/
+bool QQuickPointerTouchEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
+{
for (auto point: m_touchPoints)
- point->setGrabber(nullptr);
+ if (point->exclusiveGrabber() == handler)
+ return true;
+ return false;
}
bool QQuickPointerTouchEvent::isPressEvent() const
@@ -731,6 +1143,16 @@ bool QQuickPointerTouchEvent::isPressEvent() const
return static_cast<QTouchEvent*>(m_event)->touchPointStates() & Qt::TouchPointPressed;
}
+bool QQuickPointerTouchEvent::isUpdateEvent() const
+{
+ return static_cast<QTouchEvent*>(m_event)->touchPointStates() & (Qt::TouchPointMoved | Qt::TouchPointStationary);
+}
+
+bool QQuickPointerTouchEvent::isReleaseEvent() const
+{
+ return static_cast<QTouchEvent*>(m_event)->touchPointStates() & Qt::TouchPointReleased;
+}
+
QVector<QPointF> QQuickPointerEvent::unacceptedPressedPointScenePositions() const
{
QVector<QPointF> points;
@@ -793,15 +1215,15 @@ QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickIte
\l {QQuickEventPoint::pointId}{pointId}.
Returns nullptr if there is no point with that ID.
- \fn QQuickPointerEvent::pointById(quint64 pointId) const
+ \fn QQuickPointerEvent::pointById(int pointId) const
*/
-QQuickEventPoint *QQuickPointerMouseEvent::pointById(quint64 pointId) const {
+QQuickEventPoint *QQuickPointerMouseEvent::pointById(int pointId) const {
if (m_mousePoint && pointId == m_mousePoint->pointId())
return m_mousePoint;
return nullptr;
}
-QQuickEventPoint *QQuickPointerTouchEvent::pointById(quint64 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())
@@ -831,7 +1253,12 @@ const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int point
\internal
Make a new QTouchEvent, giving it a subset of the original touch points.
- Returns a nullptr if all points are stationary or there are no points inside the item.
+ Returns a nullptr if all points are stationary, or there are no points inside the item,
+ or none of the points were pressed inside and the item was not grabbing any of them
+ and isFiltering is false. When isFiltering is true, it is assumed that the item
+ cares about all points which are inside its bounds, because most filtering items
+ need to monitor eventpoint movements until a drag threshold is exceeded or the
+ requirements for a gesture to be recognized are met in some other way.
*/
QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool isFiltering) const
{
@@ -841,19 +1268,24 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i
// Or else just document that velocity is always scene-relative and is not scaled and rotated with the item
// but that would require changing tst_qquickwindow::touchEvent_velocity(): it expects transformed velocity
+ bool anyPressOrReleaseInside = false;
+ bool anyGrabber = false;
QMatrix4x4 transformMatrix(QQuickItemPrivate::get(item)->windowToItemTransform());
for (int i = 0; i < m_pointCount; ++i) {
auto p = m_touchPoints.at(i);
if (p->isAccepted())
continue;
// include points where item is the grabber
- bool isGrabber = p->grabber() == item;
- // include newly pressed points inside the bounds
- bool isPressInside = p->state() == QQuickEventPoint::Pressed && item->contains(item->mapFromScene(p->scenePos()));
+ bool isGrabber = p->exclusiveGrabber() == item;
+ if (isGrabber)
+ anyGrabber = true;
+ // include points inside the bounds if no other item is the grabber or if the item is filtering
+ bool isInside = item->contains(item->mapFromScene(p->scenePos()));
+ bool hasAnotherGrabber = p->exclusiveGrabber() && p->exclusiveGrabber() != item;
// filtering: (childMouseEventFilter) include points that are grabbed by children of the target item
bool grabberIsChild = false;
- auto parent = p->grabber();
+ auto parent = p->grabberItem();
while (isFiltering && parent) {
if (parent == item) {
grabberIsChild = true;
@@ -862,11 +1294,11 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i
parent = parent->parentItem();
}
- // when filtering, send points that are grabbed by a child and points that are not grabbed but inside
- bool filterRelevant = isFiltering && (grabberIsChild || (!p->grabber() && item->contains(item->mapFromScene(p->scenePos()))));
- if (!(isGrabber || isPressInside || filterRelevant))
+ bool filterRelevant = isFiltering && grabberIsChild;
+ if (!(isGrabber || (isInside && (!hasAnotherGrabber || isFiltering)) || filterRelevant))
continue;
-
+ if ((p->state() == QQuickEventPoint::Pressed || p->state() == QQuickEventPoint::Released) && isInside)
+ anyPressOrReleaseInside = true;
const QTouchEvent::TouchPoint *tp = touchPointById(p->pointId());
if (tp) {
eventStates |= tp->state();
@@ -880,7 +1312,9 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i
}
}
- if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty())
+ // Now touchPoints will have only points which are inside the item.
+ // But if none of them were just pressed inside, and the item has no other reason to care, ignore them anyway.
+ if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty() || (!anyPressOrReleaseInside && !anyGrabber && !isFiltering))
return nullptr;
// if all points have the same state, set the event type accordingly
@@ -942,7 +1376,12 @@ Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *
Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *event) {
QDebugStateSaver saver(dbg);
dbg.nospace();
- dbg << "QQuickPointerEvent(dev:";
+ dbg << "QQuickPointerEvent(";
+ if (event->isValid())
+ dbg << event->timestamp();
+ else
+ dbg << "invalid";
+ dbg << " dev:";
QtDebugUtils::formatQEnum(dbg, event->device()->type());
if (event->buttons() != Qt::NoButton) {
dbg << " buttons:";
@@ -959,7 +1398,7 @@ Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *e
Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *event) {
QDebugStateSaver saver(dbg);
dbg.nospace();
- dbg << "QQuickEventPoint(valid:" << event->isValid() << " accepted:" << event->isAccepted()
+ dbg << "QQuickEventPoint(accepted:" << event->isAccepted()
<< " state:";
QtDebugUtils::formatQEnum(dbg, event->state());
dbg << " scenePos:" << event->scenePos() << " id:" << hex << event->pointId() << dec
diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h
index 3735d68a85..0d6e06ac41 100644
--- a/src/quick/items/qquickevents_p_p.h
+++ b/src/quick/items/qquickevents_p_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -68,6 +68,7 @@ class QQuickPointerEvent;
class QQuickPointerMouseEvent;
class QQuickPointerTabletEvent;
class QQuickPointerTouchEvent;
+class QQuickPointerHandler;
class QQuickKeyEvent : public QObject
{
@@ -251,12 +252,17 @@ private:
class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject
{
Q_OBJECT
+ Q_PROPERTY(QQuickPointerEvent *event READ pointerEvent)
+ Q_PROPERTY(QPointF pos READ pos)
Q_PROPERTY(QPointF scenePos READ scenePos)
+ Q_PROPERTY(QPointF scenePressPos READ scenePressPos)
+ Q_PROPERTY(QPointF sceneGrabPos READ sceneGrabPos)
Q_PROPERTY(State state READ state)
- Q_PROPERTY(quint64 pointId READ pointId)
+ Q_PROPERTY(int pointId READ pointId)
Q_PROPERTY(qreal timeHeld READ timeHeld)
+ Q_PROPERTY(QVector2D velocity READ velocity)
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
- Q_PROPERTY(QQuickItem *grabber READ grabber WRITE setGrabber)
+ Q_PROPERTY(QObject *exclusiveGrabber READ exclusiveGrabber WRITE setExclusiveGrabber)
public:
enum State {
@@ -266,35 +272,79 @@ public:
Released = Qt::TouchPointReleased
// Canceled = Qt::TouchPointReleased << 1 // 0x10 // TODO maybe
};
- Q_ENUM(State)
+ Q_DECLARE_FLAGS(States, State)
+ Q_FLAG(States)
+
+ enum GrabState {
+ GrabPassive = 0x01,
+ UngrabPassive = 0x02,
+ CancelGrabPassive = 0x03,
+ OverrideGrabPassive = 0x04,
+ GrabExclusive = 0x10,
+ UngrabExclusive = 0x20,
+ CancelGrabExclusive = 0x30,
+ };
+ Q_ENUM(GrabState)
QQuickEventPoint(QQuickPointerEvent *parent);
- void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp);
-
- void invalidate() { m_valid = false; }
+ void reset(Qt::TouchPointState state, const QPointF &scenePos, int pointId, ulong timestamp, const QVector2D &velocity = QVector2D());
+ void localizePosition(QQuickItem *target);
QQuickPointerEvent *pointerEvent() const;
+ QPointF pos() const { return m_pos; }
QPointF scenePos() const { return m_scenePos; }
+ QPointF scenePressPos() const { return m_scenePressPos; }
+ QPointF sceneGrabPos() const { return m_sceneGrabPos; }
+ QVector2D velocity() const { return m_velocity; }
State state() const { return m_state; }
- quint64 pointId() const { return m_pointId; }
- bool isValid() const { return m_valid; }
+ int pointId() const { return m_pointId; }
qreal timeHeld() const { return (m_timestamp - m_pressTimestamp) / 1000.0; }
bool isAccepted() const { return m_accept; }
void setAccepted(bool accepted = true);
- QQuickItem *grabber() const;
- void setGrabber(QQuickItem *grabber);
+ QObject *exclusiveGrabber() const;
+ void setExclusiveGrabber(QObject *exclusiveGrabber);
+
+ QQuickItem *grabberItem() const;
+ Q_DECL_DEPRECATED QQuickItem *grabber() const { return grabberItem(); }
+ void setGrabberItem(QQuickItem *exclusiveGrabber);
+
+ QQuickPointerHandler *grabberPointerHandler() const;
+ void setGrabberPointerHandler(QQuickPointerHandler *exclusiveGrabber, bool exclusive = false);
+
+ void cancelExclusiveGrab();
+ void cancelPassiveGrab(QQuickPointerHandler *handler);
+ bool removePassiveGrabber(QQuickPointerHandler *handler);
+ void cancelAllGrabs(QQuickPointerHandler *handler);
+
+ QVector<QPointer <QQuickPointerHandler> > passiveGrabbers() const { return m_passiveGrabbers; }
+ void setPassiveGrabbers(const QVector<QPointer <QQuickPointerHandler> > &grabbers) { m_passiveGrabbers = grabbers; }
+ void clearPassiveGrabbers() { m_passiveGrabbers.clear(); }
+
+protected:
+ void cancelExclusiveGrabImpl(QTouchEvent *cancelEvent = nullptr);
private:
+ QVector2D estimatedVelocity() const;
+
+protected:
+ QPointF m_pos;
QPointF m_scenePos;
- quint64 m_pointId;
- QPointer<QQuickItem> m_grabber;
+ QPointF m_scenePressPos;
+ QPointF m_sceneGrabPos;
+ QVector2D m_velocity;
+ int m_pointId;
+ QPointer<QObject> m_exclusiveGrabber;
+ QVector<QPointer <QQuickPointerHandler> > m_passiveGrabbers;
ulong m_timestamp;
ulong m_pressTimestamp;
State m_state;
- bool m_valid : 1;
bool m_accept : 1;
- int m_reserved : 30;
+ bool m_grabberIsHandler : 1;
+ int m_reserved : 29;
+
+ friend class QQuickPointerTouchEvent;
+ friend class QQuickWindowPrivate;
Q_DISABLE_COPY(QQuickEventPoint)
};
@@ -304,6 +354,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickEventTouchPoint : public QQuickEventPoint
Q_OBJECT
Q_PROPERTY(qreal rotation READ rotation)
Q_PROPERTY(qreal pressure READ pressure)
+ Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters)
Q_PROPERTY(QPointingDeviceUniqueId uniqueId READ uniqueId)
public:
@@ -313,13 +364,17 @@ public:
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:
qreal m_rotation;
qreal m_pressure;
+ QSizeF m_ellipseDiameters;
QPointingDeviceUniqueId m_uniqueId;
+ friend class QQuickPointerTouchEvent;
+
Q_DISABLE_COPY(QQuickEventTouchPoint)
};
@@ -350,8 +405,12 @@ public: // property accessors
public: // helpers for C++ only (during event delivery)
virtual QQuickPointerEvent *reset(QEvent *ev) = 0;
+ virtual void localize(QQuickItem *target) = 0;
virtual bool isPressEvent() const = 0;
+ virtual bool isDoubleClickEvent() const { return false; }
+ virtual bool isUpdateEvent() const = 0;
+ virtual bool isReleaseEvent() const = 0;
virtual QQuickPointerMouseEvent *asPointerMouseEvent() { return nullptr; }
virtual QQuickPointerTouchEvent *asPointerTouchEvent() { return nullptr; }
virtual QQuickPointerTabletEvent *asPointerTabletEvent() { return nullptr; }
@@ -360,15 +419,18 @@ public: // helpers for C++ only (during event delivery)
virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; }
bool isValid() const { return m_event != 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); }
QVector<QPointF> unacceptedPressedPointScenePositions() const;
virtual int pointCount() const = 0;
virtual QQuickEventPoint *point(int i) const = 0;
- virtual QQuickEventPoint *pointById(quint64 pointId) const = 0;
- virtual QVector<QQuickItem *> grabbers() const = 0;
+ virtual QQuickEventPoint *pointById(int pointId) const = 0;
+ virtual QVector<QObject *> exclusiveGrabbers() const = 0;
virtual void clearGrabbers() const = 0;
+ virtual bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const = 0;
ulong timestamp() const { return m_event->timestamp(); }
@@ -389,15 +451,22 @@ public:
: QQuickPointerEvent(parent, device), m_mousePoint(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(quint64 pointId) const override;
+ QQuickEventPoint *pointById(int pointId) const override;
bool allPointsAccepted() const override;
- QVector<QQuickItem *> grabbers() 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;
QMouseEvent *asMouseEvent(const QPointF& localPos) const;
@@ -418,16 +487,22 @@ public:
{ }
QQuickPointerEvent *reset(QEvent *) override;
+ void localize(QQuickItem *target) override;
bool isPressEvent() const override;
+ bool isUpdateEvent() const override;
+ bool isReleaseEvent() const override;
QQuickPointerTouchEvent *asPointerTouchEvent() override { return this; }
const QQuickPointerTouchEvent *asPointerTouchEvent() const override { return this; }
int pointCount() const override { return m_pointCount; }
QQuickEventPoint *point(int i) const override;
- QQuickEventPoint *pointById(quint64 pointId) const override;
+ QQuickEventPoint *pointById(int pointId) const override;
const QTouchEvent::TouchPoint *touchPointById(int pointId) const;
bool allPointsAccepted() const override;
- QVector<QQuickItem *> grabbers() 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;
QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const;
QTouchEvent *touchEventForItem(QQuickItem *item, bool isFiltering = false) const;
@@ -497,13 +572,6 @@ public:
Q_ENUM(CapabilityFlag)
Q_FLAG(Capabilities)
- QQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &name, qint64 uniqueId = 0)
- : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps)
- , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name)
- , m_uniqueId(QPointingDeviceUniqueId::fromNumericId(uniqueId))
- {
- }
-
DeviceType type() const { return m_deviceType; }
PointerType pointerType() const { return m_pointerType; }
Capabilities capabilities() const { return m_capabilities; }
@@ -518,6 +586,17 @@ public:
static QQuickPointerDevice *genericMouseDevice();
static QQuickPointerDevice *tabletDevice(qint64);
+ QVector<QQuickPointerHandler *> &eventDeliveryTargets() { return m_eventDeliveryTargets; }
+
+private:
+ QQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &name, qint64 uniqueId = 0)
+ : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps)
+ , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name)
+ , m_uniqueId(QPointingDeviceUniqueId::fromNumericId(uniqueId))
+ {
+ }
+ ~QQuickPointerDevice() { }
+
private:
DeviceType m_deviceType;
PointerType m_pointerType;
@@ -526,8 +605,10 @@ private:
int m_buttonCount;
QString m_name;
QPointingDeviceUniqueId m_uniqueId;
+ QVector<QQuickPointerHandler *> m_eventDeliveryTargets; // during delivery, handlers which have already seen the event
Q_DISABLE_COPY(QQuickPointerDevice)
+ friend struct ConstructableQQuickPointerDevice;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::DeviceTypes)
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 3bd647fed6..ce584cd283 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -44,6 +44,7 @@
#include "qquickwindow_p.h"
#include "qquickevents_p_p.h"
+#include <QtQuick/private/qquickpointerhandler_p.h>
#include <QtQuick/private/qquicktransition_p.h>
#include <private/qqmlglobal_p.h>
@@ -254,6 +255,7 @@ QQuickFlickablePrivate::QQuickFlickablePrivate()
, flickBoost(1.0), fixupMode(Normal), vTime(0), visibleArea(0)
, flickableDirection(QQuickFlickable::AutoFlickDirection)
, boundsBehavior(QQuickFlickable::DragAndOvershootBounds)
+ , boundsMovement(QQuickFlickable::FollowBoundsBehavior)
, rebound(0)
{
}
@@ -268,6 +270,7 @@ void QQuickFlickablePrivate::init()
qmlobject_connect(&velocityTimeline, QQuickTimeLine, SIGNAL(completed()),
q, QQuickFlickable, SLOT(velocityTimelineCompleted()))
q->setAcceptedMouseButtons(Qt::LeftButton);
+ q->setAcceptTouchEvents(false); // rely on mouse events synthesized from touch
q->setFiltersChildMouseEvents(true);
QQuickItemPrivate *viewportPrivate = QQuickItemPrivate::get(contentItem);
viewportPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
@@ -563,18 +566,6 @@ void QQuickFlickablePrivate::updateBeginningEnd()
visibleArea->updateVisible();
}
-/*
-XXXTODO add docs describing moving, dragging, flicking properties, e.g.
-
-When the user starts dragging the Flickable, the dragging and moving properties
-will be true.
-
-If the velocity is sufficient when the drag is ended, flicking may begin.
-
-The moving properties will remain true until all dragging and flicking
-is finished.
-*/
-
/*!
\qmlsignal QtQuick::Flickable::dragStarted()
@@ -1558,6 +1549,8 @@ void QQuickFlickablePrivate::replayDelayedPress()
// If we have the grab, release before delivering the event
if (QQuickWindow *w = q->window()) {
+ QQuickWindowPrivate *wpriv = QQuickWindowPrivate::get(w);
+ wpriv->allowChildEventFiltering = false; // don't allow re-filtering during replay
replayingPressEvent = true;
if (w->mouseGrabberItem() == q)
q->ungrabMouse();
@@ -1565,6 +1558,7 @@ void QQuickFlickablePrivate::replayDelayedPress()
// Use the event handler that will take care of finding the proper item to propagate the event
QCoreApplication::sendEvent(w, mouseEvent.data());
replayingPressEvent = false;
+ wpriv->allowChildEventFiltering = true;
}
}
}
@@ -1573,16 +1567,18 @@ void QQuickFlickablePrivate::replayDelayedPress()
void QQuickFlickablePrivate::setViewportX(qreal x)
{
Q_Q(QQuickFlickable);
- if (pixelAligned)
- x = -Round(-x);
-
- contentItem->setX(x);
- if (contentItem->x() != x)
- return; // reentered
+ qreal effectiveX = pixelAligned ? -Round(-x) : x;
const qreal maxX = q->maxXExtent();
const qreal minX = q->minXExtent();
+ if (boundsMovement == int(QQuickFlickable::StopAtBounds))
+ effectiveX = qBound(maxX, effectiveX, minX);
+
+ contentItem->setX(effectiveX);
+ if (contentItem->x() != effectiveX)
+ return; // reentered
+
qreal overshoot = 0.0;
if (x <= maxX)
overshoot = maxX - x;
@@ -1598,16 +1594,18 @@ void QQuickFlickablePrivate::setViewportX(qreal x)
void QQuickFlickablePrivate::setViewportY(qreal y)
{
Q_Q(QQuickFlickable);
- if (pixelAligned)
- y = -Round(-y);
-
- contentItem->setY(y);
- if (contentItem->y() != y)
- return; // reentered
+ qreal effectiveY = pixelAligned ? -Round(-y) : y;
const qreal maxY = q->maxYExtent();
const qreal minY = q->minYExtent();
+ if (boundsMovement == int(QQuickFlickable::StopAtBounds))
+ effectiveY = qBound(maxY, effectiveY, minY);
+
+ contentItem->setY(effectiveY);
+ if (contentItem->y() != effectiveY)
+ return; // reentered
+
qreal overshoot = 0.0;
if (y <= maxY)
overshoot = maxY - y;
@@ -1867,8 +1865,9 @@ QQmlListProperty<QQuickItem> QQuickFlickable::flickableChildren()
beyond the Flickable's boundaries, or overshoot the
Flickable's boundaries when flicked.
- This enables the feeling that the edges of the view are soft,
- rather than a hard physical boundary.
+ When the \l boundsMovement is \c Flickable.FollowBoundsBehavior, a value
+ other than \c Flickable.StopAtBounds will give a feeling that the edges of
+ the view are soft, rather than a hard physical boundary.
The \c boundsBehavior can be one of:
@@ -1884,7 +1883,7 @@ QQmlListProperty<QQuickItem> QQuickFlickable::flickableChildren()
boundary when flicked.
\endlist
- \sa horizontalOvershoot, verticalOvershoot
+ \sa horizontalOvershoot, verticalOvershoot, boundsMovement
*/
QQuickFlickable::BoundsBehavior QQuickFlickable::boundsBehavior() const
{
@@ -2695,7 +2694,11 @@ void QQuickFlickablePrivate::updateVelocity()
The value is negative when the content is dragged or flicked beyond the beginning,
and positive when beyond the end; \c 0.0 otherwise.
- \sa verticalOvershoot, boundsBehavior
+ Whether the values are reported for dragging and/or flicking is determined by
+ \l boundsBehavior. The overshoot distance is reported even when \l boundsMovement
+ is \c Flickable.StopAtBounds.
+
+ \sa verticalOvershoot, boundsBehavior, boundsMovement
*/
qreal QQuickFlickable::horizontalOvershoot() const
{
@@ -2712,7 +2715,11 @@ qreal QQuickFlickable::horizontalOvershoot() const
The value is negative when the content is dragged or flicked beyond the beginning,
and positive when beyond the end; \c 0.0 otherwise.
- \sa horizontalOvershoot, boundsBehavior
+ Whether the values are reported for dragging and/or flicking is determined by
+ \l boundsBehavior. The overshoot distance is reported even when \l boundsMovement
+ is \c Flickable.StopAtBounds.
+
+ \sa horizontalOvershoot, boundsBehavior, boundsMovement
*/
qreal QQuickFlickable::verticalOvershoot() const
{
@@ -2720,6 +2727,68 @@ qreal QQuickFlickable::verticalOvershoot() const
return d->vData.overshoot;
}
+/*!
+ \qmlproperty enumeration QtQuick::Flickable::boundsMovement
+ \since 5.10
+
+ This property holds whether the flickable will give a feeling that the edges of the
+ view are soft, rather than a hard physical boundary.
+
+ The \c boundsMovement can be one of:
+
+ \list
+ \li Flickable.StopAtBounds - this allows implementing custom edge effects where the
+ contents do not follow drags or flicks beyond the bounds of the flickable. The values
+ of \l horizontalOvershoot and \l verticalOvershoot can be utilized to implement custom
+ edge effects.
+ \li Flickable.FollowBoundsBehavior (default) - whether the contents follow drags or
+ flicks beyond the bounds of the flickable is determined by \l boundsBehavior.
+ \endlist
+
+ The following example keeps the contents within bounds and instead applies a flip
+ effect when flicked over horizontal bounds:
+ \code
+ Flickable {
+ id: flickable
+ boundsMovement: Flickable.StopAtBounds
+ boundsBehavior: Flickable.DragAndOvershootBounds
+ transform: Rotation {
+ axis { x: 0; y: 1; z: 0 }
+ origin.x: flickable.width / 2
+ origin.y: flickable.height / 2
+ angle: Math.min(30, Math.max(-30, flickable.horizontalOvershoot))
+ }
+ }
+ \endcode
+
+ The following example keeps the contents within bounds and instead applies an opacity
+ effect when dragged over vertical bounds:
+ \code
+ Flickable {
+ boundsMovement: Flickable.StopAtBounds
+ boundsBehavior: Flickable.DragOverBounds
+ opacity: Math.max(0.5, 1.0 - Math.abs(verticalOvershoot) / height)
+ }
+ \endcode
+
+ \sa boundsBehavior, verticalOvershoot, horizontalOvershoot
+*/
+QQuickFlickable::BoundsMovement QQuickFlickable::boundsMovement() const
+{
+ Q_D(const QQuickFlickable);
+ return d->boundsMovement;
+}
+
+void QQuickFlickable::setBoundsMovement(BoundsMovement movement)
+{
+ Q_D(QQuickFlickable);
+ if (d->boundsMovement == movement)
+ return;
+
+ d->boundsMovement = movement;
+ emit boundsMovementChanged();
+}
+
QT_END_NAMESPACE
#include "moc_qquickflickable_p.cpp"
diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h
index 52cade1472..7558ee7df8 100644
--- a/src/quick/items/qquickflickable_p.h
+++ b/src/quick/items/qquickflickable_p.h
@@ -80,6 +80,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickFlickable : public QQuickItem
Q_PROPERTY(qreal verticalVelocity READ verticalVelocity NOTIFY verticalVelocityChanged)
Q_PROPERTY(BoundsBehavior boundsBehavior READ boundsBehavior WRITE setBoundsBehavior NOTIFY boundsBehaviorChanged)
+ Q_PROPERTY(BoundsMovement boundsMovement READ boundsMovement WRITE setBoundsMovement NOTIFY boundsMovementChanged REVISION 10)
Q_PROPERTY(QQuickTransition *rebound READ rebound WRITE setRebound NOTIFY reboundChanged)
Q_PROPERTY(qreal maximumFlickVelocity READ maximumFlickVelocity WRITE setMaximumFlickVelocity NOTIFY maximumFlickVelocityChanged)
Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged)
@@ -132,6 +133,15 @@ public:
BoundsBehavior boundsBehavior() const;
void setBoundsBehavior(BoundsBehavior);
+ enum BoundsMovement {
+ // StopAtBounds = 0x0,
+ FollowBoundsBehavior = 0x1
+ };
+ Q_ENUM(BoundsMovement)
+
+ BoundsMovement boundsMovement() const;
+ void setBoundsMovement(BoundsMovement movement);
+
QQuickTransition *rebound() const;
void setRebound(QQuickTransition *transition);
@@ -237,6 +247,7 @@ Q_SIGNALS:
void flickableDirectionChanged();
void interactiveChanged();
void boundsBehaviorChanged();
+ Q_REVISION(10) void boundsMovementChanged();
void reboundChanged();
void maximumFlickVelocityChanged();
void flickDecelerationChanged();
diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h
index 1ceff22dfc..8609a15fcd 100644
--- a/src/quick/items/qquickflickable_p_p.h
+++ b/src/quick/items/qquickflickable_p_p.h
@@ -247,6 +247,7 @@ public:
QQuickFlickableVisibleArea *visibleArea;
QQuickFlickable::FlickableDirection flickableDirection;
QQuickFlickable::BoundsBehavior boundsBehavior;
+ QQuickFlickable::BoundsMovement boundsMovement;
QQuickTransition *rebound;
void viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
diff --git a/src/quick/items/qquickgenericshadereffect.cpp b/src/quick/items/qquickgenericshadereffect.cpp
index b366071962..305ef7e778 100644
--- a/src/quick/items/qquickgenericshadereffect.cpp
+++ b/src/quick/items/qquickgenericshadereffect.cpp
@@ -546,7 +546,10 @@ void QQuickGenericShaderEffect::updateShaderVars(Shader shaderType)
// Have a QSignalMapper that emits mapped() with an index+type on each property change notify signal.
auto &sm(m_signalMappers[shaderType][i]);
if (!sm.mapper) {
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
sm.mapper = new QSignalMapper;
+QT_WARNING_POP
sm.mapper->setMapping(m_item, i | (shaderType << 16));
}
sm.active = true;
diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp
index dacb43a421..7e13e5e0e1 100644
--- a/src/quick/items/qquickimage.cpp
+++ b/src/quick/items/qquickimage.cpp
@@ -515,37 +515,41 @@ void QQuickImage::updatePaintedGeometry()
setImplicitSize(0, 0);
return;
}
- qreal w = widthValid() ? width() : d->pix.width();
- qreal widthScale = w / qreal(d->pix.width());
- qreal h = heightValid() ? height() : d->pix.height();
- qreal heightScale = h / qreal(d->pix.height());
+ const qreal pixWidth = d->pix.width() / d->devicePixelRatio;
+ const qreal pixHeight = d->pix.height() / d->devicePixelRatio;
+ const qreal w = widthValid() ? width() : pixWidth;
+ const qreal widthScale = w / pixWidth;
+ const qreal h = heightValid() ? height() : pixHeight;
+ const qreal heightScale = h / pixHeight;
if (widthScale <= heightScale) {
d->paintedWidth = w;
- d->paintedHeight = widthScale * qreal(d->pix.height());
+ d->paintedHeight = widthScale * pixHeight;
} else if (heightScale < widthScale) {
- d->paintedWidth = heightScale * qreal(d->pix.width());
+ d->paintedWidth = heightScale * pixWidth;
d->paintedHeight = h;
}
- qreal iHeight = (widthValid() && !heightValid()) ? d->paintedHeight : d->pix.height();
- qreal iWidth = (heightValid() && !widthValid()) ? d->paintedWidth : d->pix.width();
+ const qreal iHeight = (widthValid() && !heightValid()) ? d->paintedHeight : pixHeight;
+ const qreal iWidth = (heightValid() && !widthValid()) ? d->paintedWidth : pixWidth;
setImplicitSize(iWidth, iHeight);
} else if (d->fillMode == PreserveAspectCrop) {
if (!d->pix.width() || !d->pix.height())
return;
- qreal widthScale = width() / qreal(d->pix.width());
- qreal heightScale = height() / qreal(d->pix.height());
+ const qreal pixWidth = d->pix.width() / d->devicePixelRatio;
+ const qreal pixHeight = d->pix.height() / d->devicePixelRatio;
+ qreal widthScale = width() / pixWidth;
+ qreal heightScale = height() / pixHeight;
if (widthScale < heightScale) {
widthScale = heightScale;
} else if (heightScale < widthScale) {
heightScale = widthScale;
}
- d->paintedHeight = heightScale * qreal(d->pix.height());
- d->paintedWidth = widthScale * qreal(d->pix.width());
+ d->paintedHeight = heightScale * pixHeight;
+ d->paintedWidth = widthScale * pixWidth;
} else if (d->fillMode == Pad) {
- d->paintedWidth = d->pix.width();
- d->paintedHeight = d->pix.height();
+ d->paintedWidth = d->pix.width() / d->devicePixelRatio;
+ d->paintedHeight = d->pix.height() / d->devicePixelRatio;
} else {
d->paintedWidth = width();
d->paintedHeight = height();
@@ -556,7 +560,8 @@ void QQuickImage::updatePaintedGeometry()
void QQuickImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
QQuickImageBase::geometryChanged(newGeometry, oldGeometry);
- updatePaintedGeometry();
+ if (newGeometry.size() != oldGeometry.size())
+ updatePaintedGeometry();
}
QRectF QQuickImage::boundingRect() const
diff --git a/src/quick/items/qquickimage_p_p.h b/src/quick/items/qquickimage_p_p.h
index 522dbca803..afc33def0f 100644
--- a/src/quick/items/qquickimage_p_p.h
+++ b/src/quick/items/qquickimage_p_p.h
@@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE
class QQuickImageTextureProvider;
-class QQuickImagePrivate : public QQuickImageBasePrivate
+class Q_QUICK_PRIVATE_EXPORT QQuickImagePrivate : public QQuickImageBasePrivate
{
Q_DECLARE_PUBLIC(QQuickImage)
diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp
index e8200bb97f..74ee859f77 100644
--- a/src/quick/items/qquickimagebase.cpp
+++ b/src/quick/items/qquickimagebase.cpp
@@ -49,6 +49,30 @@
QT_BEGIN_NAMESPACE
+// This function gives derived classes the chance set the devicePixelRatio
+// if they're not happy with our implementation of it.
+bool QQuickImageBasePrivate::updateDevicePixelRatio(qreal targetDevicePixelRatio)
+{
+ // QQuickImageProvider and SVG can generate a high resolution image when
+ // sourceSize is set (this function is only called if it's set).
+ // If sourceSize is not set then the provider default size will be used, as usual.
+ bool setDevicePixelRatio = false;
+ if (url.scheme() == QLatin1String("image")) {
+ setDevicePixelRatio = true;
+ } else {
+ QString stringUrl = url.path(QUrl::PrettyDecoded);
+ if (stringUrl.endsWith(QLatin1String("svg")) ||
+ stringUrl.endsWith(QLatin1String("svgz"))) {
+ setDevicePixelRatio = true;
+ }
+ }
+
+ if (setDevicePixelRatio)
+ devicePixelRatio = targetDevicePixelRatio;
+
+ return setDevicePixelRatio;
+}
+
QQuickImageBase::QQuickImageBase(QQuickItem *parent)
: QQuickImplicitSizeItem(*(new QQuickImageBasePrivate), parent)
{
@@ -221,26 +245,11 @@ void QQuickImageBase::load()
QUrl loadUrl = d->url;
- // QQuickImageProvider and SVG can generate a high resolution image when
- // sourceSize is set. If sourceSize is not set then the provider default size
- // will be used, as usual.
- bool setDevicePixelRatio = false;
- if (d->sourcesize.isValid()) {
- if (loadUrl.scheme() == QLatin1String("image")) {
- setDevicePixelRatio = true;
- } else {
- QString stringUrl = loadUrl.path(QUrl::PrettyDecoded);
- if (stringUrl.endsWith(QLatin1String("svg")) ||
- stringUrl.endsWith(QLatin1String("svgz"))) {
- setDevicePixelRatio = true;
- }
- }
-
- if (setDevicePixelRatio)
- d->devicePixelRatio = targetDevicePixelRatio;
- }
+ bool updatedDevicePixelRatio = false;
+ if (d->sourcesize.isValid())
+ updatedDevicePixelRatio = d->updateDevicePixelRatio(targetDevicePixelRatio);
- if (!setDevicePixelRatio) {
+ if (!updatedDevicePixelRatio) {
// (possible) local file: loadUrl and d->devicePixelRatio will be modified if
// an "@2x" file is found.
resolve2xLocalFile(d->url, targetDevicePixelRatio, &loadUrl, &d->devicePixelRatio);
@@ -319,18 +328,18 @@ void QQuickImageBase::requestProgress(qint64 received, qint64 total)
void QQuickImageBase::itemChange(ItemChange change, const ItemChangeData &value)
{
- if (change == ItemSceneChange && value.window)
- connect(value.window, &QQuickWindow::screenChanged, this, &QQuickImageBase::handleScreenChanged);
+ Q_D(QQuickImageBase);
+ // If the screen DPI changed, reload image.
+ if (change == ItemDevicePixelRatioHasChanged && value.realValue != d->devicePixelRatio) {
+ // ### how can we get here with !qmlEngine(this)? that implies
+ // itemChange() on an item pending deletion, which seems strange.
+ if (qmlEngine(this) && isComponentComplete() && d->url.isValid()) {
+ load();
+ }
+ }
QQuickItem::itemChange(change, value);
}
-void QQuickImageBase::handleScreenChanged(QScreen* screen)
-{
- // Screen DPI might have changed, reload images on screen change.
- if (qmlEngine(this) && screen && isComponentComplete())
- load();
-}
-
void QQuickImageBase::componentComplete()
{
Q_D(QQuickImageBase);
diff --git a/src/quick/items/qquickimagebase_p.h b/src/quick/items/qquickimagebase_p.h
index 532f6ce683..54b1f789c9 100644
--- a/src/quick/items/qquickimagebase_p.h
+++ b/src/quick/items/qquickimagebase_p.h
@@ -123,7 +123,6 @@ protected:
private Q_SLOTS:
virtual void requestFinished();
void requestProgress(qint64,qint64);
- void handleScreenChanged(QScreen *screen);
private:
Q_DISABLE_COPY(QQuickImageBase)
diff --git a/src/quick/items/qquickimagebase_p_p.h b/src/quick/items/qquickimagebase_p_p.h
index d9b609c7fe..1b771166a2 100644
--- a/src/quick/items/qquickimagebase_p_p.h
+++ b/src/quick/items/qquickimagebase_p_p.h
@@ -59,7 +59,7 @@
QT_BEGIN_NAMESPACE
class QNetworkReply;
-class QQuickImageBasePrivate : public QQuickImplicitSizeItemPrivate
+class Q_QUICK_PRIVATE_EXPORT QQuickImageBasePrivate : public QQuickImplicitSizeItemPrivate
{
Q_DECLARE_PUBLIC(QQuickImageBase)
@@ -75,6 +75,8 @@ public:
{
}
+ virtual bool updateDevicePixelRatio(qreal targetDevicePixelRatio);
+
QQuickPixmap pix;
QQuickImageBase::Status status;
QUrl url;
diff --git a/src/quick/items/qquickimplicitsizeitem.cpp b/src/quick/items/qquickimplicitsizeitem.cpp
index 1996fb9489..2569b2a224 100644
--- a/src/quick/items/qquickimplicitsizeitem.cpp
+++ b/src/quick/items/qquickimplicitsizeitem.cpp
@@ -45,29 +45,11 @@ QT_BEGIN_NAMESPACE
/*!
\internal
- The purpose of QQuickImplicitSizeItem is not immediately clear, as both
- the implicit size properties and signals exist on QQuickItem. However,
- for some items - where the implicit size has an underlying meaning (such as
- Image, where the implicit size represents the real size of the image)
- having implicit size writable is an undesirable thing.
-
- QQuickImplicitSizeItem redefines the properties as being readonly.
- Unfortunately, this also means they need to redefine the change signals.
- See QTBUG-30258 for more information.
+ QQuickImplicitSizeItem redefines the implicitWidth and implicitHeight
+ properties as readonly, as some items (e.g. Image, where the implicit size
+ represents the real size of the image) should not be able to have their
+ implicit size modified.
*/
-void QQuickImplicitSizeItemPrivate::implicitWidthChanged()
-{
- Q_Q(QQuickImplicitSizeItem);
- QQuickItemPrivate::implicitWidthChanged();
- emit q->implicitWidthChanged2();
-}
-
-void QQuickImplicitSizeItemPrivate::implicitHeightChanged()
-{
- Q_Q(QQuickImplicitSizeItem);
- QQuickItemPrivate::implicitHeightChanged();
- emit q->implicitHeightChanged2();
-}
QQuickImplicitSizeItem::QQuickImplicitSizeItem(QQuickImplicitSizeItemPrivate &dd, QQuickItem *parent)
: QQuickItem(dd, parent)
diff --git a/src/quick/items/qquickimplicitsizeitem_p.h b/src/quick/items/qquickimplicitsizeitem_p.h
index 75b04449f8..8ae8f9f447 100644
--- a/src/quick/items/qquickimplicitsizeitem_p.h
+++ b/src/quick/items/qquickimplicitsizeitem_p.h
@@ -60,16 +60,12 @@ class QQuickImplicitSizeItemPrivate;
class Q_QUICK_PRIVATE_EXPORT QQuickImplicitSizeItem : public QQuickItem
{
Q_OBJECT
- Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged2)
- Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged2)
+ Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged)
+ Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged)
protected:
QQuickImplicitSizeItem(QQuickImplicitSizeItemPrivate &dd, QQuickItem *parent);
-Q_SIGNALS:
- Q_REVISION(1) void implicitWidthChanged2();
- Q_REVISION(1) void implicitHeightChanged2();
-
private:
Q_DISABLE_COPY(QQuickImplicitSizeItem)
Q_DECLARE_PRIVATE(QQuickImplicitSizeItem)
diff --git a/src/quick/items/qquickimplicitsizeitem_p_p.h b/src/quick/items/qquickimplicitsizeitem_p_p.h
index 2c42fa62f2..0495cf87e1 100644
--- a/src/quick/items/qquickimplicitsizeitem_p_p.h
+++ b/src/quick/items/qquickimplicitsizeitem_p_p.h
@@ -65,9 +65,6 @@ public:
QQuickImplicitSizeItemPrivate()
{
}
-
- void implicitWidthChanged() Q_DECL_OVERRIDE;
- void implicitHeightChanged() Q_DECL_OVERRIDE;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 549575a97f..9f2d543387 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/qquickpointerhandler_p.h>
#include <private/qv4engine_p.h>
#include <private/qv4object_p.h>
@@ -163,7 +164,7 @@ void QQuickTransform::update()
}
QQuickContents::QQuickContents(QQuickItem *item)
-: m_item(item), m_x(0), m_y(0), m_width(0), m_height(0)
+: m_item(item)
{
}
@@ -178,8 +179,8 @@ QQuickContents::~QQuickContents()
bool QQuickContents::calcHeight(QQuickItem *changed)
{
- qreal oldy = m_y;
- qreal oldheight = m_height;
+ qreal oldy = m_contents.y();
+ qreal oldheight = m_contents.height();
if (changed) {
qreal top = oldy;
@@ -189,8 +190,8 @@ bool QQuickContents::calcHeight(QQuickItem *changed)
bottom = y + changed->height();
if (y < top)
top = y;
- m_y = top;
- m_height = bottom - top;
+ m_contents.setY(top);
+ m_contents.setHeight(bottom - top);
} else {
qreal top = std::numeric_limits<qreal>::max();
qreal bottom = -std::numeric_limits<qreal>::max();
@@ -204,17 +205,17 @@ bool QQuickContents::calcHeight(QQuickItem *changed)
top = y;
}
if (!children.isEmpty())
- m_y = top;
- m_height = qMax(bottom - top, qreal(0.0));
+ m_contents.setY(top);
+ m_contents.setHeight(qMax(bottom - top, qreal(0.0)));
}
- return (m_height != oldheight || m_y != oldy);
+ return (m_contents.height() != oldheight || m_contents.y() != oldy);
}
bool QQuickContents::calcWidth(QQuickItem *changed)
{
- qreal oldx = m_x;
- qreal oldwidth = m_width;
+ qreal oldx = m_contents.x();
+ qreal oldwidth = m_contents.width();
if (changed) {
qreal left = oldx;
@@ -224,8 +225,8 @@ bool QQuickContents::calcWidth(QQuickItem *changed)
right = x + changed->width();
if (x < left)
left = x;
- m_x = left;
- m_width = right - left;
+ m_contents.setX(left);
+ m_contents.setWidth(right - left);
} else {
qreal left = std::numeric_limits<qreal>::max();
qreal right = -std::numeric_limits<qreal>::max();
@@ -239,11 +240,11 @@ bool QQuickContents::calcWidth(QQuickItem *changed)
left = x;
}
if (!children.isEmpty())
- m_x = left;
- m_width = qMax(right - left, qreal(0.0));
+ m_contents.setX(left);
+ m_contents.setWidth(qMax(right - left, qreal(0.0)));
}
- return (m_width != oldwidth || m_x != oldx);
+ return (m_contents.width() != oldwidth || m_contents.x() != oldx);
}
void QQuickContents::complete()
@@ -2138,6 +2139,9 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
\value ItemAntialiasingHasChanged The antialiasing has changed. The current
(boolean) value can be found in QQuickItem::antialiasing.
+
+ \value ItemEnabledHasChanged The item's enabled state has changed.
+ ItemChangeData::boolValue contains the new enabled state. (since Qt 5.10)
*/
/*!
@@ -3186,6 +3190,11 @@ QQuickItemPrivate::QQuickItemPrivate()
, antialiasingValid(false)
, isTabFence(false)
, replayingPressEvent(false)
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ , touchEnabled(true)
+#else
+ , touchEnabled(false)
+#endif
, dirtyAttributes(0)
, nextDirtyItem(0)
, prevDirtyItem(0)
@@ -3237,7 +3246,14 @@ void QQuickItemPrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
} else {
if (o->inherits("QGraphicsItem"))
qWarning("Cannot add a QtQuick 1.0 item (%s) into a QtQuick 2.0 scene!", o->metaObject()->className());
- else {
+ else if (QQuickPointerHandler *pointerHandler = qmlobject_cast<QQuickPointerHandler *>(o)) {
+ Q_ASSERT(pointerHandler->parentItem() == that);
+ // Accept all buttons, and leave filtering to pointerEvent() and/or user JS,
+ // because there can be multiple handlers...
+ that->setAcceptedMouseButtons(Qt::AllButtons);
+ QQuickItemPrivate *p = QQuickItemPrivate::get(that);
+ p->extra.value().pointerHandlers.append(pointerHandler);
+ } else {
QQuickWindow *thisWindow = qmlobject_cast<QQuickWindow *>(o);
QQuickItem *item = that;
QQuickWindow *itemWindow = that->window();
@@ -5102,6 +5118,28 @@ void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event)
}
/*!
+ \internal
+ Deliver the \a event to all PointerHandlers which are in the pre-determined
+ eventDeliveryTargets() vector. If \a avoidExclusiveGrabber is true, it skips
+ delivery to any handler which is the exclusive grabber of any point within this event
+ (because delivery to exclusive grabbers is handled separately).
+*/
+bool QQuickItemPrivate::handlePointerEvent(QQuickPointerEvent *event, bool avoidExclusiveGrabber)
+{
+ bool delivered = false;
+ QVector<QQuickPointerHandler *> &eventDeliveryTargets = event->device()->eventDeliveryTargets();
+ if (extra.isAllocated()) {
+ for (QQuickPointerHandler *handler : extra->pointerHandlers) {
+ if ((!avoidExclusiveGrabber || !event->hasExclusiveGrabber(handler)) && !eventDeliveryTargets.contains(handler)) {
+ handler->handlePointerEvent(event);
+ delivered = true;
+ }
+ }
+ }
+ return delivered;
+}
+
+/*!
Called when \a change occurs for this item.
\a value contains extra information relating to the change, when
@@ -5927,6 +5965,7 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec
scope, q, Qt::OtherFocusReason, QQuickWindowPrivate::DontChangeFocusProperty | QQuickWindowPrivate::DontChangeSubFocusItem);
}
+ itemChange(QQuickItem::ItemEnabledHasChanged, effectiveEnable);
emit q->enabledChanged();
}
@@ -6099,6 +6138,18 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
}
break;
}
+ case QQuickItem::ItemEnabledHasChanged: {
+ q->itemChange(change, data);
+ if (!changeListeners.isEmpty()) {
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
+ for (const QQuickItemPrivate::ChangeListener &change : listeners) {
+ if (change.types & QQuickItemPrivate::Enabled) {
+ change.listener->itemEnabledChanged(q);
+ }
+ }
+ }
+ break;
+ }
case QQuickItem::ItemParentHasChanged: {
q->itemChange(change, data);
if (!changeListeners.isEmpty()) {
@@ -6763,8 +6814,27 @@ bool QQuickItem::heightValid() const
}
/*!
- \internal
- */
+ \since 5.10
+
+ Returns the size of the item.
+
+ \sa setSize, width, height
+ */
+
+QSizeF QQuickItem::size() const
+{
+ Q_D(const QQuickItem);
+ return QSizeF(d->width, d->height);
+}
+
+
+/*!
+ \since 5.10
+
+ Sets the size of the item to \a size.
+
+ \sa size, setWidth, setHeight
+ */
void QQuickItem::setSize(const QSizeF &size)
{
Q_D(QQuickItem);
@@ -7154,6 +7224,32 @@ void QQuickItem::setAcceptHoverEvents(bool enabled)
d->setHasHoverInChild(enabled);
}
+/*!
+ Returns whether touch events are accepted by this item.
+
+ The default value is false.
+
+ If this is false, then the item will not receive any touch events through
+ the touchEvent() function.
+*/
+bool QQuickItem::acceptTouchEvents() const
+{
+ Q_D(const QQuickItem);
+ return d->touchEnabled;
+}
+
+/*!
+ If \a enabled is true, this sets the item to accept touch events;
+ otherwise, touch events are not accepted by this item.
+
+ \sa acceptTouchEvents()
+*/
+void QQuickItem::setAcceptTouchEvents(bool accept)
+{
+ Q_D(QQuickItem);
+ d->touchEnabled = accept;
+}
+
void QQuickItemPrivate::setHasCursorInChild(bool hasCursor)
{
#if QT_CONFIG(cursor)
@@ -7886,6 +7982,7 @@ QQuickItemLayer::QQuickItemLayer(QQuickItem *item)
, m_effect(0)
, m_effectSource(0)
, m_textureMirroring(QQuickShaderEffectSource::MirrorVertically)
+ , m_samples(0)
{
}
@@ -7960,6 +8057,7 @@ void QQuickItemLayer::activate()
m_effectSource->setWrapMode(m_wrapMode);
m_effectSource->setFormat(m_format);
m_effectSource->setTextureMirroring(m_textureMirroring);
+ m_effectSource->setSamples(m_samples);
if (m_effectComponent)
activateEffect();
@@ -8253,6 +8351,44 @@ void QQuickItemLayer::setTextureMirroring(QQuickShaderEffectSource::TextureMirro
}
/*!
+ \qmlproperty enumeration QtQuick::Item::layer.samples
+ \since 5.10
+
+ This property allows requesting multisampled rendering in the layer.
+
+ By default multisampling is enabled whenever multisampling is
+ enabled for the entire window, assuming the scenegraph renderer in
+ use and the underlying graphics API supports this.
+
+ By setting the value to 2, 4, etc. multisampled rendering can be requested
+ for a part of the scene without enabling multisampling for the entire
+ scene. This way multisampling is applied only to a given subtree, which can
+ lead to significant performance gains since multisampling is not applied to
+ other parts of the scene.
+
+ \note Enabling multisampling can be potentially expensive regardless of the
+ layer's size, as it incurs a hardware and driver dependent performance and
+ memory cost.
+
+ \note This property is only functional when support for multisample
+ renderbuffers and framebuffer blits is available. Otherwise the value is
+ silently ignored.
+ */
+
+void QQuickItemLayer::setSamples(int count)
+{
+ if (m_samples == count)
+ return;
+
+ m_samples = count;
+
+ if (m_effectSource)
+ m_effectSource->setSamples(m_samples);
+
+ emit samplesChanged(count);
+}
+
+/*!
\qmlproperty string QtQuick::Item::layer.samplerName
Holds the name of the effect's source texture property.
@@ -8401,19 +8537,19 @@ struct QQuickItemWrapper : public QObjectWrapper {
struct QQuickItemWrapper : public QV4::QObjectWrapper {
V4_OBJECT2(QQuickItemWrapper, QV4::QObjectWrapper)
- static void markObjects(QV4::Heap::Base *that, QV4::ExecutionEngine *e);
+ static void markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack);
};
DEFINE_OBJECT_VTABLE(QQuickItemWrapper);
-void QQuickItemWrapper::markObjects(QV4::Heap::Base *that, QV4::ExecutionEngine *e)
+void QQuickItemWrapper::markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack)
{
QObjectWrapper::Data *This = static_cast<QObjectWrapper::Data *>(that);
if (QQuickItem *item = static_cast<QQuickItem*>(This->object())) {
for (QQuickItem *child : qAsConst(QQuickItemPrivate::get(item)->childItems))
- QV4::QObjectWrapper::markWrapper(child, e);
+ QV4::QObjectWrapper::markWrapper(child, markStack);
}
- QV4::QObjectWrapper::markObjects(that, e);
+ QV4::QObjectWrapper::markObjects(that, markStack);
}
quint64 QQuickItemPrivate::_q_createJSWrapper(QV4::ExecutionEngine *engine)
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index c9494d91bd..25641f16f9 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -173,7 +173,8 @@ public:
ItemActiveFocusHasChanged, // value.boolValue
ItemRotationHasChanged, // value.realValue
ItemAntialiasingHasChanged, // value.boolValue
- ItemDevicePixelRatioHasChanged // value.realValue
+ ItemDevicePixelRatioHasChanged, // value.realValue
+ ItemEnabledHasChanged // value.boolValue
};
union ItemChangeData {
@@ -237,6 +238,7 @@ public:
void setImplicitHeight(qreal);
qreal implicitHeight() const;
+ QSizeF size() const;
void setSize(const QSizeF &size);
TransformOrigin transformOrigin() const;
@@ -291,6 +293,8 @@ public:
void setAcceptedMouseButtons(Qt::MouseButtons buttons);
bool acceptHoverEvents() const;
void setAcceptHoverEvents(bool enabled);
+ bool acceptTouchEvents() const;
+ void setAcceptTouchEvents(bool accept);
#if QT_CONFIG(cursor)
QCursor cursor() const;
@@ -447,6 +451,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_resourceObjectDeleted(QObject *))
Q_PRIVATE_SLOT(d_func(), quint64 _q_createJSWrapper(QV4::ExecutionEngine *))
+ friend class QQuickEventPoint;
friend class QQuickWindow;
friend class QQuickWindowPrivate;
friend class QSGRenderer;
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index c0c9bd46bd..d1aaf6026b 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -87,6 +87,7 @@ class QQuickItemKeyFilter;
class QQuickLayoutMirroringAttached;
class QQuickEnterKeyAttached;
class QQuickScreenAttached;
+class QQuickPointerHandler;
class QQuickContents : public QQuickItemChangeListener
{
@@ -94,7 +95,7 @@ public:
QQuickContents(QQuickItem *item);
~QQuickContents();
- QRectF rectF() const { return QRectF(m_x, m_y, m_width, m_height); }
+ QRectF rectF() const { return m_contents; }
inline void calcGeometry(QQuickItem *changed = 0);
void complete();
@@ -112,10 +113,7 @@ private:
void updateRect();
QQuickItem *m_item;
- qreal m_x;
- qreal m_y;
- qreal m_width;
- qreal m_height;
+ QRectF m_contents;
};
void QQuickContents::calcGeometry(QQuickItem *changed)
@@ -152,6 +150,8 @@ class QQuickItemLayer : public QObject, public QQuickItemChangeListener
Q_PROPERTY(QByteArray samplerName READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QQmlComponent *effect READ effect WRITE setEffect NOTIFY effectChanged)
Q_PROPERTY(QQuickShaderEffectSource::TextureMirroring textureMirroring READ textureMirroring WRITE setTextureMirroring NOTIFY textureMirroringChanged)
+ Q_PROPERTY(int samples READ samples WRITE setSamples NOTIFY samplesChanged)
+
public:
QQuickItemLayer(QQuickItem *item);
~QQuickItemLayer();
@@ -189,6 +189,9 @@ public:
QQuickShaderEffectSource::TextureMirroring textureMirroring() const { return m_textureMirroring; }
void setTextureMirroring(QQuickShaderEffectSource::TextureMirroring mirroring);
+ int samples() const { return m_samples; }
+ void setSamples(int count);
+
QQuickShaderEffectSource *effectSource() const { return m_effectSource; }
void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) Q_DECL_OVERRIDE;
@@ -213,6 +216,7 @@ Q_SIGNALS:
void formatChanged(QQuickShaderEffectSource::Format format);
void sourceRectChanged(const QRectF &sourceRect);
void textureMirroringChanged(QQuickShaderEffectSource::TextureMirroring mirroring);
+ void samplesChanged(int count);
private:
friend class QQuickTransformAnimatorJob;
@@ -237,6 +241,7 @@ private:
QQuickItem *m_effect;
QQuickShaderEffectSource *m_effectSource;
QQuickShaderEffectSource::TextureMirroring m_textureMirroring;
+ int m_samples;
};
#endif
@@ -317,7 +322,8 @@ public:
Children = 0x40,
Rotation = 0x80,
ImplicitWidth = 0x100,
- ImplicitHeight = 0x200
+ ImplicitHeight = 0x200,
+ Enabled = 0x400,
};
Q_DECLARE_FLAGS(ChangeTypes, ChangeType)
@@ -344,6 +350,7 @@ public:
QQuickLayoutMirroringAttached* layoutDirectionAttached;
QQuickEnterKeyAttached *enterKeyAttached;
QQuickItemKeyFilter *keyHandler;
+ QVector<QQuickPointerHandler *> pointerHandlers;
#if QT_CONFIG(quick_shadereffect)
mutable QQuickItemLayer *layer;
#endif
@@ -433,6 +440,7 @@ public:
// focus chain and prevents tabbing outside.
bool isTabFence:1;
bool replayingPressEvent:1;
+ bool touchEnabled:1;
enum DirtyType {
TransformOrigin = 0x00000001,
@@ -561,6 +569,8 @@ public:
#endif
void deliverShortcutOverrideEvent(QKeyEvent *);
+ virtual bool handlePointerEvent(QQuickPointerEvent *, bool avoidExclusiveGrabber = false);
+
bool isTransparentForPositioner() const;
void setTransparentForPositioner(bool trans);
diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp
index 714a1a9012..d4d346def9 100644
--- a/src/quick/items/qquickitemanimation.cpp
+++ b/src/quick/items/qquickitemanimation.cpp
@@ -327,7 +327,7 @@ QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &act
}
if (scale != 0)
- rotation = qAtan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI;
+ rotation = qRadiansToDegrees(qAtan2(transform.m12() / scale, transform.m11() / scale));
else {
qmlWarning(this) << QQuickParentAnimation::tr("Unable to preserve appearance under scale of 0");
ok = false;
diff --git a/src/quick/items/qquickitemchangelistener_p.h b/src/quick/items/qquickitemchangelistener_p.h
index 83c69a9330..cb0af75c4c 100644
--- a/src/quick/items/qquickitemchangelistener_p.h
+++ b/src/quick/items/qquickitemchangelistener_p.h
@@ -125,6 +125,7 @@ public:
virtual void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF & /* oldGeometry */) {}
virtual void itemSiblingOrderChanged(QQuickItem *) {}
virtual void itemVisibilityChanged(QQuickItem *) {}
+ virtual void itemEnabledChanged(QQuickItem *) {}
virtual void itemOpacityChanged(QQuickItem *) {}
virtual void itemDestroyed(QQuickItem *) {}
virtual void itemChildAdded(QQuickItem *, QQuickItem * /* child */ ) {}
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index 5f6d44b54d..e6321e9365 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -376,6 +376,12 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickFlickable, 9>(uri, 2, 9, "Flickable");
qmlRegisterType<QQuickMouseArea, 9>(uri, 2, 9, "MouseArea");
+
+#if QT_CONFIG(quick_path)
+ qmlRegisterType<QQuickPathArc, 2>(uri, 2, 9, "PathArc");
+ qmlRegisterType<QQuickPathMove>(uri, 2, 9, "PathMove");
+#endif
+
qmlRegisterType<QQuickText, 9>(uri, 2, 9, "Text");
qmlRegisterType<QQuickTextInput, 9>(uri, 2, 9, "TextInput");
qmlRegisterType<QQuickTouchPoint>(uri, 2, 9, "TouchPoint");
@@ -385,6 +391,12 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterUncreatableType<QQuickBasePositioner, 9>(uri, 2, 9, "Positioner",
QStringLiteral("Positioner is an abstract type that is only available as an attached property."));
#endif
+
+#if QT_CONFIG(quick_shadereffect)
+ qmlRegisterType<QQuickShaderEffectSource, 2>(uri, 2, 9, "ShaderEffectSource");
+#endif
+
+ qmlRegisterType<QQuickFlickable, 10>(uri, 2, 10, "Flickable");
}
static void initResources()
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp
index d8bad7d793..96f34ef276 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -85,6 +85,7 @@ void QQuickMouseAreaPrivate::init()
{
Q_Q(QQuickMouseArea);
q->setAcceptedMouseButtons(Qt::LeftButton);
+ q->setAcceptTouchEvents(false); // rely on mouse events synthesized from touch
q->setFiltersChildMouseEvents(true);
if (qmlVisualTouchDebugging()) {
q->setFlag(QQuickItem::ItemHasContents);
diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp
index d4c447a384..54136b1bbf 100644
--- a/src/quick/items/qquickmultipointtoucharea.cpp
+++ b/src/quick/items/qquickmultipointtoucharea.cpp
@@ -418,6 +418,7 @@ QQuickMultiPointTouchArea::QQuickMultiPointTouchArea(QQuickItem *parent)
if (qmlVisualTouchDebugging()) {
setFlag(QQuickItem::ItemHasContents);
}
+ setAcceptTouchEvents(true);
#ifdef Q_OS_OSX
setAcceptHoverEvents(true); // needed to enable touch events on mouse hover.
#endif
diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp
index 4fcfe04b55..c3e0ba05bd 100644
--- a/src/quick/items/qquickopenglshadereffect.cpp
+++ b/src/quick/items/qquickopenglshadereffect.cpp
@@ -230,7 +230,7 @@ void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QQuickItem *item,
auto mapper = signalMappers[shaderType].at(i);
void *a = mapper;
QObjectPrivate::disconnect(item, mapper->signalIndex(), &a);
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
if (source) {
if (item->window())
@@ -272,7 +272,7 @@ void QQuickOpenGLShaderEffectCommon::connectPropertySignals(QQuickItem *item,
qWarning("QQuickOpenGLShaderEffect: '%s' does not have a matching property!", d.name.constData());
}
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
if (source) {
if (item->window())
@@ -335,6 +335,7 @@ void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item,
Q_ASSERT(decl == UniformQualifier);
const int sampLen = sizeof("sampler2D") - 1;
+ const int sampExtLen = sizeof("samplerExternalOES") - 1;
const int opLen = sizeof("qt_Opacity") - 1;
const int matLen = sizeof("qt_Matrix") - 1;
const int srLen = sizeof("qt_SubRect_") - 1;
@@ -357,8 +358,12 @@ void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item,
mapper = new QtPrivate::MappedSlotObject([this, mappedId](){
this->mappedPropertyChanged(mappedId);
});
- bool sampler = typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0;
- d.specialType = sampler ? UniformData::Sampler : UniformData::None;
+ if (typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0)
+ d.specialType = UniformData::Sampler;
+ else if (typeLength == sampExtLen && qstrncmp("samplerExternalOES", s + typeIndex, sampExtLen) == 0)
+ d.specialType = UniformData::SamplerExternal;
+ else
+ d.specialType = UniformData::None;
d.setValueFromProperty(item, itemMetaObject);
}
uniformData[shaderType].append(d);
@@ -451,7 +456,8 @@ void QQuickOpenGLShaderEffectCommon::updateMaterial(QQuickOpenGLShaderEffectNode
int textureProviderCount = 0;
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
for (int i = 0; i < uniformData[shaderType].size(); ++i) {
- if (uniformData[shaderType].at(i).specialType == UniformData::Sampler)
+ if (uniformData[shaderType].at(i).specialType == UniformData::Sampler ||
+ uniformData[shaderType].at(i).specialType == UniformData::SamplerExternal)
++textureProviderCount;
}
material->uniforms[shaderType] = uniformData[shaderType];
@@ -474,7 +480,7 @@ void QQuickOpenGLShaderEffectCommon::updateMaterial(QQuickOpenGLShaderEffectNode
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
for (int i = 0; i < uniformData[shaderType].size(); ++i) {
const UniformData &d = uniformData[shaderType].at(i);
- if (d.specialType != UniformData::Sampler)
+ if (d.specialType != UniformData::Sampler && d.specialType != UniformData::SamplerExternal)
continue;
QSGTextureProvider *oldProvider = material->textureProviders.at(index);
QSGTextureProvider *newProvider = 0;
@@ -513,7 +519,7 @@ void QQuickOpenGLShaderEffectCommon::updateWindow(QQuickWindow *window)
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
for (int i = 0; i < uniformData[shaderType].size(); ++i) {
const UniformData &d = uniformData[shaderType].at(i);
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
if (source)
QQuickItemPrivate::get(source)->refWindow(window);
@@ -524,7 +530,7 @@ void QQuickOpenGLShaderEffectCommon::updateWindow(QQuickWindow *window)
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
for (int i = 0; i < uniformData[shaderType].size(); ++i) {
const UniformData &d = uniformData[shaderType].at(i);
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
if (source)
QQuickItemPrivate::get(source)->derefWindow();
@@ -539,7 +545,7 @@ void QQuickOpenGLShaderEffectCommon::sourceDestroyed(QObject *object)
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
for (int i = 0; i < uniformData[shaderType].size(); ++i) {
UniformData &d = uniformData[shaderType][i];
- if (d.specialType == UniformData::Sampler && d.value.canConvert<QObject *>()) {
+ if ((d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) && d.value.canConvert<QObject *>()) {
if (qvariant_cast<QObject *>(d.value) == object)
d.value = QVariant();
}
@@ -554,7 +560,7 @@ static bool qquick_uniqueInUniformData(QQuickItem *source, const QVector<QQuickO
if (s == typeToSkip && i == indexToSkip)
continue;
const QQuickOpenGLShaderEffectMaterial::UniformData &d = uniformData[s][i];
- if (d.specialType == QQuickOpenGLShaderEffectMaterial::UniformData::Sampler && qvariant_cast<QObject *>(d.value) == source)
+ if ((d.specialType == QQuickOpenGLShaderEffectMaterial::UniformData::Sampler || d.specialType == QQuickOpenGLShaderEffectMaterial::UniformData::SamplerExternal) && qvariant_cast<QObject *>(d.value) == source)
return false;
}
}
@@ -568,7 +574,7 @@ void QQuickOpenGLShaderEffectCommon::propertyChanged(QQuickItem *item,
Key::ShaderType shaderType = Key::ShaderType(mappedId >> 16);
int index = mappedId & 0xffff;
UniformData &d = uniformData[shaderType][index];
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
if (source) {
if (item->window())
diff --git a/src/quick/items/qquickopenglshadereffectnode.cpp b/src/quick/items/qquickopenglshadereffectnode.cpp
index e1ea98641d..5dbfee73cb 100644
--- a/src/quick/items/qquickopenglshadereffectnode.cpp
+++ b/src/quick/items/qquickopenglshadereffectnode.cpp
@@ -47,6 +47,10 @@
#include <QtCore/qmutex.h>
#include <QtGui/qopenglfunctions.h>
+#ifndef GL_TEXTURE_EXTERNAL_OES
+#define GL_TEXTURE_EXTERNAL_OES 0x8D65
+#endif
+
QT_BEGIN_NAMESPACE
static bool hasAtlasTexture(const QVector<QSGTextureProvider *> &textureProviders)
@@ -124,7 +128,7 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
for (int i = 0; i < material->uniforms[shaderType].size(); ++i) {
const UniformData &d = material->uniforms[shaderType].at(i);
QByteArray name = d.name;
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
program()->setUniformValue(d.name.constData(), textureProviderIndex++);
// We don't need to store the sampler uniform locations, since their values
// only need to be set once. Look for the "qt_SubRect_" uniforms instead.
@@ -143,7 +147,7 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
for (int i = 0; i < material->uniforms[shaderType].size(); ++i) {
const UniformData &d = material->uniforms[shaderType].at(i);
int loc = m_uniformLocs[shaderType].at(i);
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
int idx = textureProviderIndex++;
functions->glActiveTexture(GL_TEXTURE0 + idx);
if (QSGTextureProvider *provider = material->textureProviders.at(idx)) {
@@ -164,7 +168,10 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
continue;
}
}
- functions->glBindTexture(GL_TEXTURE_2D, 0);
+ if (d.specialType == UniformData::Sampler)
+ functions->glBindTexture(GL_TEXTURE_2D, 0);
+ else if (d.specialType == UniformData::SamplerExternal)
+ functions->glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
} else if (d.specialType == UniformData::Opacity) {
program()->setUniformValue(loc, state.opacity());
} else if (d.specialType == UniformData::Matrix) {
@@ -396,7 +403,7 @@ bool QQuickOpenGLShaderEffectMaterial::UniformData::operator == (const UniformDa
if (name != other.name)
return false;
- if (specialType == UniformData::Sampler) {
+ if (specialType == UniformData::Sampler || specialType == UniformData::SamplerExternal) {
// We can't check the source objects as these live in the GUI thread,
// so return true here and rely on the textureProvider check for
// equality of these..
diff --git a/src/quick/items/qquickopenglshadereffectnode_p.h b/src/quick/items/qquickopenglshadereffectnode_p.h
index aea28e6612..784294d9eb 100644
--- a/src/quick/items/qquickopenglshadereffectnode_p.h
+++ b/src/quick/items/qquickopenglshadereffectnode_p.h
@@ -90,7 +90,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectMaterial : public QSGMateri
public:
struct UniformData
{
- enum SpecialType { None, Sampler, SubRect, Opacity, Matrix };
+ enum SpecialType { None, Sampler, SamplerExternal, SubRect, Opacity, Matrix };
QByteArray name;
QVariant value;
diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp
index 3f6ce7b8ba..476acd3a3e 100644
--- a/src/quick/items/qquickpincharea.cpp
+++ b/src/quick/items/qquickpincharea.cpp
@@ -288,6 +288,7 @@ QQuickPinchArea::QQuickPinchArea(QQuickItem *parent)
{
Q_D(QQuickPinchArea);
d->init();
+ setAcceptTouchEvents(true);
#ifdef Q_OS_OSX
setAcceptHoverEvents(true); // needed to enable touch events on mouse hover.
#endif
diff --git a/src/quick/items/qquickpositioners.cpp b/src/quick/items/qquickpositioners.cpp
index 06b18d714b..e752e2538f 100644
--- a/src/quick/items/qquickpositioners.cpp
+++ b/src/quick/items/qquickpositioners.cpp
@@ -44,9 +44,6 @@
#include <QtQml/qqmlinfo.h>
#include <QtCore/qcoreapplication.h>
-#include <QtQuick/private/qquickstate_p.h>
-#include <QtQuick/private/qquickstategroup_p.h>
-#include <private/qquickstatechangescript_p.h>
#include <QtQuick/private/qquicktransition_p.h>
QT_BEGIN_NAMESPACE
@@ -1137,7 +1134,7 @@ public:
: QQuickBasePositionerPrivate()
{}
- void effectiveLayoutDirectionChange()
+ void effectiveLayoutDirectionChange() override
{
Q_Q(QQuickRow);
// For RTL layout the positioning changes when the width changes.
@@ -1436,7 +1433,7 @@ public:
: QQuickBasePositionerPrivate()
{}
- void effectiveLayoutDirectionChange()
+ void effectiveLayoutDirectionChange() override
{
Q_Q(QQuickGrid);
// For RTL layout the positioning changes when the width changes.
@@ -2023,7 +2020,7 @@ public:
: QQuickBasePositionerPrivate(), flow(QQuickFlow::LeftToRight)
{}
- void effectiveLayoutDirectionChange()
+ void effectiveLayoutDirectionChange() override
{
Q_Q(QQuickFlow);
// Don't postpone, as it might be the only trigger for visible changes.
diff --git a/src/quick/items/qquickpositioners_p.h b/src/quick/items/qquickpositioners_p.h
index ae6e795794..9ae7029d69 100644
--- a/src/quick/items/qquickpositioners_p.h
+++ b/src/quick/items/qquickpositioners_p.h
@@ -58,7 +58,6 @@ QT_REQUIRE_CONFIG(quick_positioners);
#include "qquickimplicitsizeitem_p.h"
#include "qquickitemviewtransition_p.h"
-#include <QtQuick/private/qquickstate_p.h>
#include <private/qpodvector_p.h>
#include <QtCore/qobject.h>
diff --git a/src/quick/items/qquickpositioners_p_p.h b/src/quick/items/qquickpositioners_p_p.h
index 6dd84e6098..0be4c56df6 100644
--- a/src/quick/items/qquickpositioners_p_p.h
+++ b/src/quick/items/qquickpositioners_p_p.h
@@ -58,9 +58,6 @@ QT_REQUIRE_CONFIG(quick_positioners);
#include "qquickpositioners_p.h"
#include "qquickimplicitsizeitem_p_p.h"
-#include <QtQuick/private/qquickstate_p.h>
-#include <private/qquicktransitionmanager_p_p.h>
-#include <private/qquickstatechangescript_p.h>
#include <private/qlazilyallocated_p.h>
#include <QtCore/qobject.h>
diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp
index 8ac982f74c..9308553a79 100644
--- a/src/quick/items/qquickrectangle.cpp
+++ b/src/quick/items/qquickrectangle.cpp
@@ -90,6 +90,7 @@ void QQuickPen::setWidth(qreal w)
m_width = w;
m_valid = m_color.alpha() && (qRound(m_width) >= 1 || (!m_aligned && m_width > 0));
+ static_cast<QQuickItem*>(parent())->update();
emit penChanged();
}
@@ -102,6 +103,7 @@ void QQuickPen::setColor(const QColor &c)
{
m_color = c;
m_valid = m_color.alpha() && (qRound(m_width) >= 1 || (!m_aligned && m_width > 0));
+ static_cast<QQuickItem*>(parent())->update();
emit penChanged();
}
@@ -116,6 +118,7 @@ void QQuickPen::setPixelAligned(bool aligned)
return;
m_aligned = aligned;
m_valid = m_color.alpha() && (qRound(m_width) >= 1 || (!m_aligned && m_width > 0));
+ static_cast<QQuickItem*>(parent())->update();
emit penChanged();
}
@@ -322,6 +325,9 @@ QQuickRectangle::QQuickRectangle(QQuickItem *parent)
: QQuickItem(*(new QQuickRectanglePrivate), parent)
{
setFlag(ItemHasContents);
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ setAcceptTouchEvents(false);
+#endif
}
void QQuickRectangle::doUpdate()
@@ -356,7 +362,11 @@ void QQuickRectangle::doUpdate()
QQuickPen *QQuickRectangle::border()
{
Q_D(QQuickRectangle);
- return d->getPen();
+ if (!d->pen) {
+ d->pen = new QQuickPen;
+ QQml_setParent_noEvent(d->pen, this);
+ }
+ return d->pen;
}
/*!
diff --git a/src/quick/items/qquickrectangle_p_p.h b/src/quick/items/qquickrectangle_p_p.h
index 50d5817951..3c1aaf7661 100644
--- a/src/quick/items/qquickrectangle_p_p.h
+++ b/src/quick/items/qquickrectangle_p_p.h
@@ -70,7 +70,6 @@ public:
~QQuickRectanglePrivate()
{
- delete pen;
}
QColor color;
@@ -78,20 +77,6 @@ public:
QQuickPen *pen;
qreal radius;
static int doUpdateSlotIdx;
-
- QQuickPen *getPen() {
- if (!pen) {
- Q_Q(QQuickRectangle);
- pen = new QQuickPen;
- static int penChangedSignalIdx = -1;
- if (penChangedSignalIdx < 0)
- penChangedSignalIdx = QMetaMethod::fromSignal(&QQuickPen::penChanged).methodIndex();
- if (doUpdateSlotIdx < 0)
- doUpdateSlotIdx = QQuickRectangle::staticMetaObject.indexOfSlot("doUpdate()");
- QMetaObject::connect(pen, penChangedSignalIdx, q, doUpdateSlotIdx);
- }
- return pen;
- }
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickscreen.cpp b/src/quick/items/qquickscreen.cpp
index 9b54b7fba9..6a3eab957e 100644
--- a/src/quick/items/qquickscreen.cpp
+++ b/src/quick/items/qquickscreen.cpp
@@ -100,6 +100,27 @@ QT_BEGIN_NAMESPACE
The y coordinate of the screen within the virtual desktop.
*/
/*!
+ \qmlattachedproperty string Screen::manufacturer
+ \readonly
+ \since 5.10
+
+ The manufacturer of the screen.
+*/
+/*!
+ \qmlattachedproperty string Screen::model
+ \readonly
+ \since 5.10
+
+ The model of the screen.
+*/
+/*!
+ \qmlattachedproperty string Screen::serialNumber
+ \readonly
+ \since 5.10
+
+ The serial number of the screen.
+*/
+/*!
\qmlattachedproperty int Screen::width
\readonly
@@ -234,6 +255,27 @@ QString QQuickScreenInfo::name() const
return m_screen->name();
}
+QString QQuickScreenInfo::manufacturer() const
+{
+ if (!m_screen)
+ return QString();
+ return m_screen->manufacturer();
+}
+
+QString QQuickScreenInfo::model() const
+{
+ if (!m_screen)
+ return QString();
+ return m_screen->model();
+}
+
+QString QQuickScreenInfo::serialNumber() const
+{
+ if (!m_screen)
+ return QString();
+ return m_screen->serialNumber();
+}
+
int QQuickScreenInfo::width() const
{
if (!m_screen)
@@ -335,6 +377,12 @@ void QQuickScreenInfo::setWrappedScreen(QScreen *screen)
}
if (!oldScreen || screen->name() != oldScreen->name())
emit nameChanged();
+ if (!oldScreen || screen->manufacturer() != oldScreen->manufacturer())
+ emit manufacturerChanged();
+ if (!oldScreen || screen->model() != oldScreen->model())
+ emit modelChanged();
+ if (!oldScreen || screen->serialNumber() != oldScreen->serialNumber())
+ emit serialNumberChanged();
if (!oldScreen || screen->orientation() != oldScreen->orientation())
emit orientationChanged();
if (!oldScreen || screen->primaryOrientation() != oldScreen->primaryOrientation())
diff --git a/src/quick/items/qquickscreen_p.h b/src/quick/items/qquickscreen_p.h
index 99e1466631..e9db07d14c 100644
--- a/src/quick/items/qquickscreen_p.h
+++ b/src/quick/items/qquickscreen_p.h
@@ -68,6 +68,9 @@ class Q_AUTOTEST_EXPORT QQuickScreenInfo : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
+ Q_PROPERTY(QString manufacturer READ manufacturer NOTIFY manufacturerChanged REVISION 10)
+ Q_PROPERTY(QString model READ model NOTIFY modelChanged REVISION 10)
+ Q_PROPERTY(QString serialNumber READ serialNumber NOTIFY serialNumberChanged REVISION 10)
Q_PROPERTY(int width READ width NOTIFY widthChanged)
Q_PROPERTY(int height READ height NOTIFY heightChanged)
Q_PROPERTY(int desktopAvailableWidth READ desktopAvailableWidth NOTIFY desktopGeometryChanged)
@@ -87,6 +90,9 @@ public:
QQuickScreenInfo(QObject *parent = nullptr, QScreen *wrappedScreen = nullptr);
QString name() const;
+ QString manufacturer() const;
+ QString model() const;
+ QString serialNumber() const;
int width() const;
int height() const;
int desktopAvailableWidth() const;
@@ -104,6 +110,9 @@ public:
Q_SIGNALS:
void nameChanged();
+ Q_REVISION(10) void manufacturerChanged();
+ Q_REVISION(10) void modelChanged();
+ Q_REVISION(10) void serialNumberChanged();
void widthChanged();
void heightChanged();
void desktopGeometryChanged();
diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp
index 6b1b16618a..f61bad1179 100644
--- a/src/quick/items/qquickshadereffectsource.cpp
+++ b/src/quick/items/qquickshadereffectsource.cpp
@@ -189,6 +189,7 @@ QQuickShaderEffectSource::QQuickShaderEffectSource(QQuickItem *parent)
, m_sourceItem(0)
, m_textureSize(0, 0)
, m_format(RGBA)
+ , m_samples(0)
, m_live(true)
, m_hideSource(false)
, m_mipmap(false)
@@ -582,6 +583,44 @@ void QQuickShaderEffectSource::setTextureMirroring(TextureMirroring mirroring)
}
/*!
+ \qmlproperty int QtQuick::ShaderEffectSource::samples
+ \since 5.10
+
+ This property allows requesting multisampled rendering.
+
+ By default multisampling is enabled whenever multisampling is enabled for
+ the entire window, assuming the scenegraph renderer in use and the
+ underlying graphics API supports this.
+
+ By setting the value to 2, 4, etc. multisampled rendering can be requested
+ for a part of the scene without enabling multisampling for the entire
+ scene. This way multisampling is applied only to a given subtree, which can
+ lead to significant performance gains since multisampling is not applied to
+ other parts of the scene.
+
+ \note Enabling multisampling can be potentially expensive regardless of the
+ layer's size, as it incurs a hardware and driver dependent performance and
+ memory cost.
+
+ \note This property is only functional when support for multisample
+ renderbuffers and framebuffer blits is available. Otherwise the value is
+ silently ignored.
+ */
+int QQuickShaderEffectSource::samples() const
+{
+ return m_samples;
+}
+
+void QQuickShaderEffectSource::setSamples(int count)
+{
+ if (count == m_samples)
+ return;
+ m_samples = count;
+ update();
+ emit samplesChanged();
+}
+
+/*!
\qmlmethod QtQuick::ShaderEffectSource::scheduleUpdate()
Schedules a re-rendering of the texture for the next frame.
@@ -683,6 +722,7 @@ QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaint
m_texture->setHasMipmaps(m_mipmap);
m_texture->setMirrorHorizontal(m_textureMirroring & MirrorHorizontally);
m_texture->setMirrorVertical(m_textureMirroring & MirrorVertically);
+ m_texture->setSamples(m_samples);
if (m_grab)
m_texture->scheduleUpdate();
diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h
index 5e7e354feb..d9f9079a3d 100644
--- a/src/quick/items/qquickshadereffectsource_p.h
+++ b/src/quick/items/qquickshadereffectsource_p.h
@@ -88,6 +88,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectSource : public QQuickItem, publi
Q_PROPERTY(bool mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged)
Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged)
Q_PROPERTY(TextureMirroring textureMirroring READ textureMirroring WRITE setTextureMirroring NOTIFY textureMirroringChanged REVISION 1)
+ Q_PROPERTY(int samples READ samples WRITE setSamples NOTIFY samplesChanged REVISION 2)
public:
enum WrapMode {
@@ -150,6 +151,9 @@ public:
Q_INVOKABLE void scheduleUpdate();
+ int samples() const;
+ void setSamples(int count);
+
Q_SIGNALS:
void wrapModeChanged();
void sourceItemChanged();
@@ -161,6 +165,7 @@ Q_SIGNALS:
void mipmapChanged();
void recursiveChanged();
void textureMirroringChanged();
+ void samplesChanged();
void scheduledUpdateCompleted();
@@ -185,6 +190,7 @@ private:
QRectF m_sourceRect;
QSize m_textureSize;
Format m_format;
+ int m_samples;
uint m_live : 1;
uint m_hideSource : 1;
uint m_mipmap : 1;
diff --git a/src/quick/items/qquickstateoperations.cpp b/src/quick/items/qquickstateoperations.cpp
index b40a9e2843..a4ce13a199 100644
--- a/src/quick/items/qquickstateoperations.cpp
+++ b/src/quick/items/qquickstateoperations.cpp
@@ -101,7 +101,7 @@ void QQuickParentChangePrivate::doChange(QQuickItem *targetParent, QQuickItem *s
}
if (scale != 0)
- rotation = qAtan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI;
+ rotation = qRadiansToDegrees(qAtan2(transform.m12() / scale, transform.m11() / scale));
else {
qmlWarning(q) << QQuickParentChange::tr("Unable to preserve appearance under scale of 0");
ok = false;
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index 6d73af80fe..5ca0bb5f79 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -269,9 +269,6 @@ void QQuickTextPrivate::updateLayout()
formatModifiesFontSize = fontSizeModified;
multilengthEos = -1;
} else {
- layout.clearFormats();
- if (elideLayout)
- elideLayout->clearFormats();
QString tmp = text;
multilengthEos = tmp.indexOf(QLatin1Char('\x9c'));
if (multilengthEos != -1)
@@ -359,8 +356,8 @@ void QQuickTextPrivate::updateSize()
}
if (!requireImplicitSize) {
- emit q->implicitWidthChanged();
- emit q->implicitHeightChanged();
+ implicitWidthChanged();
+ implicitHeightChanged();
// if the implicitWidth is used, then updateSize() has already been called (recursively)
if (requireImplicitSize)
return;
@@ -384,6 +381,7 @@ void QQuickTextPrivate::updateSize()
updateBaseline(fm.ascent(), q->height() - fontHeight - vPadding);
q->setImplicitSize(hPadding, fontHeight + vPadding);
layedOutTextRect = QRectF(0, 0, 0, fontHeight);
+ advance = QSizeF();
emit q->contentSizeChanged();
updateType = UpdatePaintNode;
q->update();
@@ -457,8 +455,26 @@ void QQuickTextPrivate::updateSize()
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();
+ }
}
+
if (layedOutTextRect.size() != previousSize)
emit q->contentSizeChanged();
updateType = UpdatePaintNode;
@@ -613,6 +629,13 @@ QString QQuickTextPrivate::elidedText(qreal lineWidth, const QTextLine &line, QT
}
}
+void QQuickTextPrivate::clearFormats()
+{
+ layout.clearFormats();
+ if (elideLayout)
+ elideLayout->clearFormats();
+}
+
/*!
Lays out the QQuickTextPrivate::layout QTextLayout in the constraints of the QQuickText.
@@ -968,6 +991,16 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
br.moveTop(0);
+ // Find the advance of the text layout
+ if (layout.lineCount() > 0) {
+ QTextLine firstLine = layout.lineAt(0);
+ QTextLine lastLine = layout.lineAt(layout.lineCount() - 1);
+ advance = QSizeF(lastLine.horizontalAdvance(),
+ lastLine.y() - firstLine.y());
+ } else {
+ advance = QSizeF();
+ }
+
if (!horizontalFit && !verticalFit)
break;
@@ -1026,12 +1059,15 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
if (eos != multilengthEos)
truncated = true;
+ assignedFont = QFontInfo(font).family();
+
if (elide) {
if (!elideLayout) {
elideLayout = new QTextLayout;
elideLayout->setCacheEnabled(true);
}
- if (styledText) {
+ QTextEngine *engine = layout.engine();
+ if (engine && engine->hasFormats()) {
QVector<QTextLayout::FormatRange> formats;
switch (elideMode) {
case QQuickText::ElideRight:
@@ -1470,6 +1506,19 @@ QQuickText::~QQuickText()
Text { text: "Hello"; renderType: Text.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting }
\endqml
*/
+
+/*!
+ \qmlproperty bool QtQuick::Text::font.kerning
+ \since 5.10
+
+ Enables or disables the kerning OpenType feature when shaping the text. This may improve performance
+ when creating or changing the text, at the expense of some cosmetic features. The default value
+ is true.
+
+ \qml
+ Text { text: "OATS FLAVOUR WAY"; font.kerning: false }
+ \endqml
+*/
QFont QQuickText::font() const
{
Q_D(const QQuickText);
@@ -1570,6 +1619,7 @@ void QQuickText::setText(const QString &n)
d->extra->doc->setText(n);
d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
} else {
+ d->clearFormats();
d->rightToLeftText = d->text.isRightToLeft();
}
d->determineHorizontalAlignment();
@@ -2060,6 +2110,7 @@ void QQuickText::setTextFormat(TextFormat format)
d->extra->doc->setText(d->text);
d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft();
} else {
+ d->clearFormats();
d->rightToLeftText = d->text.isRightToLeft();
d->textHasChanged = true;
}
@@ -2365,6 +2416,12 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data
void QQuickText::updatePolish()
{
Q_D(QQuickText);
+ // If the fonts used for rendering are different from the ones used in the GUI thread,
+ // it means we will get warnings and corrupted text. If this case is detected, we need
+ // to update the text layout before creating the scenegraph nodes.
+ if (!d->assignedFont.isEmpty() && QFontInfo(d->font).family() != d->assignedFont)
+ d->polishSize = true;
+
if (d->polishSize) {
d->updateSize();
d->polishSize = false;
@@ -3055,6 +3112,24 @@ QJSValue QQuickText::fontInfo() const
return value;
}
+/*!
+ \qmlproperty size QtQuick::Text::advance
+ \since 5.10
+
+ The distance, in pixels, from the baseline origin of the first
+ character of the text item, to the baseline origin of the first
+ character in a text item occurring directly after this one
+ in a text flow.
+
+ Note that the advance can be negative if the text flows from
+ the right to the left.
+*/
+QSizeF QQuickText::advance() const
+{
+ Q_D(const QQuickText);
+ return d->advance;
+}
+
QT_END_NAMESPACE
#include "moc_qquicktext_p.cpp"
diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h
index b190738cfb..a56bcdb87b 100644
--- a/src/quick/items/qquicktext_p.h
+++ b/src/quick/items/qquicktext_p.h
@@ -99,6 +99,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem
Q_PROPERTY(qreal bottomPadding READ bottomPadding WRITE setBottomPadding RESET resetBottomPadding NOTIFY bottomPaddingChanged REVISION 6)
Q_PROPERTY(QJSValue fontInfo READ fontInfo NOTIFY fontInfoChanged REVISION 9)
+ Q_PROPERTY(QSizeF advance READ advance NOTIFY contentSizeChanged REVISION 10)
public:
QQuickText(QQuickItem *parent=0);
@@ -251,6 +252,7 @@ public:
void resetBottomPadding();
QJSValue fontInfo() const;
+ QSizeF advance() const;
Q_SIGNALS:
void textChanged(const QString &text);
diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h
index 6456750359..87f5162384 100644
--- a/src/quick/items/qquicktext_p_p.h
+++ b/src/quick/items/qquicktext_p_p.h
@@ -85,10 +85,12 @@ public:
int lineHeightOffset() const;
QString elidedText(qreal lineWidth, const QTextLine &line, QTextLine *nextLine = 0) const;
void elideFormats(int start, int length, int offset, QVector<QTextLayout::FormatRange> *elidedFormats);
+ void clearFormats();
void processHoverEvent(QHoverEvent *event);
QRectF layedOutTextRect;
+ QSizeF advance;
struct ExtraData {
ExtraData();
@@ -152,6 +154,8 @@ public:
QQuickText::RenderType renderType;
UpdateType updateType;
+ QString assignedFont;
+
bool maximumLineCountValid:1;
bool updateOnComponentComplete:1;
bool richText:1;
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index 61d610520f..f22c5f00be 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -356,6 +356,19 @@ QString QQuickTextEdit::text() const
*/
/*!
+ \qmlproperty bool QtQuick::TextEdit::font.kerning
+ \since 5.10
+
+ Enables or disables the kerning OpenType feature when shaping the text. This may improve performance
+ when creating or changing the text, at the expense of some cosmetic features. The default value
+ is true.
+
+ \qml
+ TextEdit { text: "OATS FLAVOUR WAY"; kerning: font.false }
+ \endqml
+*/
+
+/*!
\qmlproperty string QtQuick::TextEdit::text
The text to display. If the text format is AutoText the text edit will
@@ -3038,6 +3051,32 @@ void QQuickTextEdit::resetBottomPadding()
}
/*!
+ \qmlproperty real QtQuick::TextEdit::tabStopDistance
+ \since 5.10
+
+ The default distance, in device units, between tab stops.
+
+ \sa QTextOption::setTabStop()
+*/
+int QQuickTextEdit::tabStopDistance() const
+{
+ Q_D(const QQuickTextEdit);
+ return d->document->defaultTextOption().tabStop();
+}
+
+void QQuickTextEdit::setTabStopDistance(qreal distance)
+{
+ Q_D(QQuickTextEdit);
+ QTextOption textOptions = d->document->defaultTextOption();
+ if (textOptions.tabStop() == distance)
+ return;
+
+ textOptions.setTabStop(distance);
+ d->document->setDefaultTextOption(textOptions);
+ emit tabStopDistanceChanged(distance);
+}
+
+/*!
\qmlmethod QtQuick::TextEdit::clear()
\since 5.7
diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h
index c8d3515be1..23033edb88 100644
--- a/src/quick/items/qquicktextedit_p.h
+++ b/src/quick/items/qquicktextedit_p.h
@@ -111,6 +111,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem
Q_PROPERTY(qreal rightPadding READ rightPadding WRITE setRightPadding RESET resetRightPadding NOTIFY rightPaddingChanged REVISION 6)
Q_PROPERTY(qreal bottomPadding READ bottomPadding WRITE setBottomPadding RESET resetBottomPadding NOTIFY bottomPaddingChanged REVISION 6)
Q_PROPERTY(QString preeditText READ preeditText NOTIFY preeditTextChanged REVISION 7)
+ Q_PROPERTY(qreal tabStopDistance READ tabStopDistance WRITE setTabStopDistance NOTIFY tabStopDistanceChanged REVISION 10)
public:
QQuickTextEdit(QQuickItem *parent=0);
@@ -296,6 +297,9 @@ public:
void setBottomPadding(qreal padding);
void resetBottomPadding();
+ int tabStopDistance() const;
+ void setTabStopDistance(qreal distance);
+
Q_SIGNALS:
void textChanged();
Q_REVISION(7) void preeditTextChanged();
@@ -340,6 +344,7 @@ Q_SIGNALS:
Q_REVISION(6) void leftPaddingChanged();
Q_REVISION(6) void rightPaddingChanged();
Q_REVISION(6) void bottomPaddingChanged();
+ Q_REVISION(10) void tabStopDistanceChanged(qreal distance);
public Q_SLOTS:
void selectAll();
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index 28b089da49..06ad0a9dbf 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -378,6 +378,19 @@ QString QQuickTextInputPrivate::realText() const
TextInput { text: "Hello"; renderType: TextInput.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting }
\endqml
*/
+
+/*!
+ \qmlproperty bool QtQuick::TextInput::font.kerning
+ \since 5.10
+
+ Enables or disables the kerning OpenType feature when shaping the text. This may improve performance
+ when creating or changing the text, at the expense of some cosmetic features. The default value
+ is true.
+
+ \qml
+ TextInput { text: "OATS FLAVOUR WAY"; font.kerning: false }
+ \endqml
+*/
QFont QQuickTextInput::font() const
{
Q_D(const QQuickTextInput);
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index c124150b8d..7b331251f0 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -46,6 +46,7 @@
#include "qquickevents_p_p.h"
#include <private/qquickdrag_p.h>
+#include <private/qquickpointerhandler_p.h>
#include <QtQuick/private/qsgrenderer_p.h>
#include <QtQuick/private/qsgtexture_p.h>
@@ -499,6 +500,8 @@ QQuickWindowPrivate::QQuickWindowPrivate()
, persistentSceneGraph(true)
, lastWheelEventAccepted(false)
, componentCompleted(true)
+ , allowChildEventFiltering(true)
+ , allowDoubleClick(true)
, lastFocusReason(Qt::OtherFocusReason)
, renderTarget(0)
, renderTargetId(0)
@@ -652,7 +655,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve
if (!item->contains(pos))
break;
- qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << item;
+ qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << p.id() << "->" << item;
QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event.data(), item, false));
// Send a single press and see if that's accepted
@@ -664,7 +667,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve
if (!q->mouseGrabberItem())
item->grabMouse();
auto pointerEventPoint = pointerEvent->pointById(p.id());
- pointerEventPoint->setGrabber(item);
+ pointerEventPoint->setGrabberItem(item);
if (checkIfDoubleClicked(event->timestamp())) {
QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event.data(), item, false));
@@ -688,7 +691,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve
QCoreApplication::sendEvent(item, me.data());
event->setAccepted(me->isAccepted());
if (me->isAccepted()) {
- qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << mouseGrabberItem;
+ qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << p.id() << "->" << mouseGrabberItem;
}
return event->isAccepted();
} else {
@@ -744,39 +747,42 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber)
if (q->mouseGrabberItem() == grabber)
return;
- qCDebug(DBG_MOUSE_TARGET) << "grabber" << q->mouseGrabberItem() << "->" << grabber;
QQuickItem *oldGrabber = q->mouseGrabberItem();
- bool fromTouch = false;
+ qCDebug(DBG_MOUSE_TARGET) << "grabber" << oldGrabber << "->" << grabber;
if (grabber && touchMouseId != -1 && touchMouseDevice) {
// update the touch item for mouse touch id to the new grabber
qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << touchMouseId << "->" << q->mouseGrabberItem();
auto point = pointerEventInstance(touchMouseDevice)->pointById(touchMouseId);
- if (point)
- point->setGrabber(grabber);
- fromTouch = true;
+ if (point) {
+ auto originalEvent = pointerEventInstance(point->pointerEvent()->device());
+ for (int i = 0; i < originalEvent->pointCount(); ++i)
+ originalEvent->point(i)->cancelExclusiveGrab();
+ point->setGrabberItem(grabber);
+ for (auto handler : point->passiveGrabbers())
+ point->cancelPassiveGrab(handler);
+ }
} else {
QQuickPointerEvent *event = pointerEventInstance(QQuickPointerDevice::genericMouseDevice());
Q_ASSERT(event->pointCount() == 1);
- event->point(0)->setGrabber(grabber);
+ auto point = event->point(0);
+ point->setGrabberItem(grabber);
+ for (auto handler : point->passiveGrabbers())
+ point->cancelPassiveGrab(handler);
}
+
if (oldGrabber) {
QEvent e(QEvent::UngrabMouse);
QSet<QQuickItem *> hasFiltered;
- if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered)) {
+ if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered))
oldGrabber->mouseUngrabEvent();
- if (fromTouch)
- oldGrabber->touchUngrabEvent();
- }
}
}
-void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids)
+void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector<int> &ids)
{
- QSet<QQuickItem*> ungrab;
for (int i = 0; i < ids.count(); ++i) {
- // FIXME: deprecate this function, we need a device
int id = ids.at(i);
if (Q_UNLIKELY(id < 0)) {
qWarning("ignoring grab of touchpoint %d", id);
@@ -784,11 +790,11 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int
}
if (id == touchMouseId) {
auto point = pointerEventInstance(touchMouseDevice)->pointById(id);
- auto touchMouseGrabber = point->grabber();
+ auto touchMouseGrabber = point->grabberItem();
if (touchMouseGrabber) {
- point->setGrabber(nullptr);
+ point->setExclusiveGrabber(nullptr);
touchMouseGrabber->mouseUngrabEvent();
- ungrab.insert(touchMouseGrabber);
+ touchMouseGrabber->touchUngrabEvent();
touchMouseDevice = nullptr;
touchMouseId = -1;
}
@@ -800,19 +806,23 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int
auto point = pointerEventInstance(device)->pointById(id);
if (!point)
continue;
- QQuickItem *oldGrabber = point->grabber();
+ QObject *oldGrabber = point->exclusiveGrabber();
if (oldGrabber == grabber)
continue;
-
- point->setGrabber(grabber);
- if (oldGrabber)
- ungrab.insert(oldGrabber);
+ point->setExclusiveGrabber(grabber);
}
}
- for (QQuickItem *oldGrabber : qAsConst(ungrab))
- oldGrabber->touchUngrabEvent();
}
+/*!
+ Ungrabs all touchpoint grabs and/or the mouse grab from the given item \a grabber.
+ This should not be called when processing a release event - that's redundant.
+ It is called in other cases, when the points may not be released, but the item
+ nevertheless must lose its grab due to becoming disabled, invisible, etc.
+ QQuickEventPoint::setGrabberItem() calls touchUngrabEvent() when all points are released,
+ but if not all points are released, it cannot be sure whether to call touchUngrabEvent()
+ or not; so we have to do it here.
+*/
void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch)
{
Q_Q(QQuickWindow);
@@ -821,17 +831,19 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to
setMouseGrabber(nullptr);
}
if (Q_LIKELY(touch)) {
+ bool ungrab = false;
const auto touchDevices = QQuickPointerDevice::touchDevices();
for (auto device : touchDevices) {
auto pointerEvent = pointerEventInstance(device);
for (int i = 0; i < pointerEvent->pointCount(); ++i) {
- if (pointerEvent->point(i)->grabber() == grabber) {
- pointerEvent->point(i)->setGrabber(nullptr);
- // FIXME send ungrab event only once
- grabber->touchUngrabEvent();
+ if (pointerEvent->point(i)->exclusiveGrabber() == grabber) {
+ pointerEvent->point(i)->setGrabberItem(nullptr);
+ ungrab = true;
}
}
}
+ if (ungrab)
+ grabber->touchUngrabEvent();
}
}
@@ -1495,12 +1507,12 @@ QQuickItem *QQuickWindow::mouseGrabberItem() const
QQuickPointerEvent *event = d->pointerEventInstance(d->touchMouseDevice);
auto point = event->pointById(d->touchMouseId);
Q_ASSERT(point);
- return point->grabber();
+ return point->grabberItem();
}
QQuickPointerEvent *event = d->pointerEventInstance(QQuickPointerDevice::genericMouseDevice());
Q_ASSERT(event->pointCount());
- return event->point(0)->grabber();
+ return event->point(0)->grabberItem();
}
@@ -1649,36 +1661,75 @@ QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *t
void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent)
{
- Q_Q(QQuickWindow);
auto point = pointerEvent->point(0);
lastMousePosition = point->scenePos();
- QQuickItem *grabber = point->grabber();
- if (grabber) {
- // if the update consists of changing button state, then don't accept it
- // unless the button is one in which the item is interested
- if (pointerEvent->button() != Qt::NoButton
- && grabber->acceptedMouseButtons()
- && !(grabber->acceptedMouseButtons() & pointerEvent->button())) {
- pointerEvent->setAccepted(false);
- return;
- }
- // send update
- QPointF localPos = grabber->mapFromScene(lastMousePosition);
- auto me = pointerEvent->asMouseEvent(localPos);
- me->accept();
- q->sendEvent(grabber, me);
- point->setAccepted(me->isAccepted());
+ if (point->exclusiveGrabber()) {
+ bool mouseIsReleased = (point->state() == QQuickEventPoint::Released && pointerEvent->buttons() == Qt::NoButton);
+ if (auto grabber = point->grabberItem()) {
+ if (sendFilteredPointerEvent(pointerEvent, grabber))
+ return;
+ // if the grabber is an Item:
+ // if the update consists of changing button state, don't accept it unless
+ // the button is one in which the grabber is interested
+ if (pointerEvent->button() != Qt::NoButton && grabber->acceptedMouseButtons()
+ && !(grabber->acceptedMouseButtons() & pointerEvent->button())) {
+ pointerEvent->setAccepted(false);
+ return;
+ }
+
+ // send update
+ QPointF localPos = grabber->mapFromScene(lastMousePosition);
+ auto me = pointerEvent->asMouseEvent(localPos);
+ me->accept();
+ QCoreApplication::sendEvent(grabber, me);
+ point->setAccepted(me->isAccepted());
- // release event, make sure to ungrab if there still is a grabber
- if (me->type() == QEvent::MouseButtonRelease && !me->buttons() && q->mouseGrabberItem())
- q->mouseGrabberItem()->ungrabMouse();
+ // release event: ungrab if no buttons are pressed anymore
+ if (mouseIsReleased)
+ setMouseGrabber(nullptr);
+ } else {
+ // if the grabber is not an Item, it must be a PointerHandler
+ auto handler = point->grabberPointerHandler();
+ pointerEvent->localize(handler->parentItem());
+ if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem()))
+ handler->handlePointerEvent(pointerEvent);
+ if (mouseIsReleased) {
+ point->setGrabberPointerHandler(nullptr, true);
+ point->clearPassiveGrabbers();
+ }
+ }
} else {
- // send initial press
bool delivered = false;
if (pointerEvent->isPressEvent()) {
- QSet<QQuickItem*> hasFiltered;
- delivered = deliverPressEvent(pointerEvent, &hasFiltered);
+ // send initial press
+ delivered = deliverPressOrReleaseEvent(pointerEvent);
+ } else if (pointerEvent->device()->type() == QQuickPointerDevice::Mouse) {
+ // if this is an update or release from an actual mouse,
+ // and the point wasn't grabbed, deliver only to PointerHandlers:
+ // passive grabbers first, then the rest
+ const QVector<QQuickPointerHandler *> &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets();
+ for (auto handler : point->passiveGrabbers()) {
+ // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically
+ if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) {
+ if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem()))
+ handler->handlePointerEvent(pointerEvent);
+ }
+ }
+ // 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->scenePos(), false, false);
+ for (QQuickItem *item : targetItems) {
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ pointerEvent->localize(item);
+ if (!sendFilteredPointerEvent(pointerEvent, item)) {
+ if (itemPrivate->handlePointerEvent(pointerEvent, true)) // avoid re-delivering to grabbers
+ delivered = true;
+ }
+ if (point->exclusiveGrabber())
+ break;
+ }
+ }
}
if (!delivered)
@@ -1881,19 +1932,16 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
Q_Q(QQuickWindow);
// A TouchCancel event will typically not contain any points.
- // Deliver it to all items that have active touches.
+ // Deliver it to all items and handlers that have active touches.
QQuickPointerEvent *pointerEvent = pointerEventInstance(QQuickPointerDevice::touchDevice(event->device()));
- QVector<QQuickItem *> grabbers = pointerEvent->grabbers();
-
- for (QQuickItem *grabber: qAsConst(grabbers)) {
- q->sendEvent(grabber, event);
- }
+ for (int i = 0; i < pointerEvent->pointCount(); ++i)
+ pointerEvent->point(i)->cancelExclusiveGrabImpl(event);
touchMouseId = -1;
touchMouseDevice = nullptr;
if (q->mouseGrabberItem())
q->mouseGrabberItem()->ungrabMouse();
- // The next touch event can only be a TouchBegin so clean up.
+ // The next touch event can only be a TouchBegin, so clean up.
pointerEvent->clearGrabbers();
return true;
}
@@ -2030,8 +2078,6 @@ void QQuickWindow::mouseReleaseEvent(QMouseEvent *event)
void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
{
- Q_Q(QQuickWindow);
-
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
event->accept();
return;
@@ -2052,7 +2098,8 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
case QEvent::MouseButtonDblClick:
Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick,
event->button(), event->buttons());
- deliverPointerEvent(pointerEventInstance(event));
+ if (allowDoubleClick)
+ deliverPointerEvent(pointerEventInstance(event));
break;
case QEvent::MouseMove:
Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove,
@@ -2064,7 +2111,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
updateCursor(event->windowPos());
#endif
- if (!q->mouseGrabberItem()) {
+ if (!pointerEventInstance(QQuickPointerDevice::genericMouseDevice())->point(0)->exclusiveGrabber()) {
QPointF last = lastMousePosition.isNull() ? event->windowPos() : lastMousePosition;
lastMousePosition = event->windowPos();
@@ -2075,7 +2122,6 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
accepted = clearHover(event->timestamp());
}
event->setAccepted(accepted);
- return;
}
deliverPointerEvent(pointerEventInstance(event));
break;
@@ -2195,7 +2241,10 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event)
// check if item or any of its child items contain the point
// FIXME: should this be iterative instead of recursive?
-QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos, bool checkMouseButtons) const
+// 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 *> targets;
auto itemPrivate = QQuickItemPrivate::get(item);
@@ -2213,13 +2262,16 @@ QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, cons
auto childPrivate = QQuickItemPrivate::get(child);
if (!child->isVisible() || !child->isEnabled() || childPrivate->culled)
continue;
- targets << pointerTargets(child, scenePos, false);
+ targets << pointerTargets(child, scenePos, checkMouseButtons, checkAcceptsTouch);
}
- if (item->contains(itemPos) && (!checkMouseButtons || itemPrivate->acceptedMouseButtons())) {
- // add this item last - children take precedence
- targets << item;
- }
+ bool relevant = item->contains(itemPos);
+ if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton)
+ relevant = false;
+ if (relevant && checkAcceptsTouch && !(item->acceptTouchEvents() || item->acceptedMouseButtons()))
+ relevant = false;
+ if (relevant)
+ targets << item; // add this item last: children take precedence
return targets;
}
@@ -2249,11 +2301,12 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event)
{
qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent();
- QSet<QQuickItem *> hasFiltered;
if (event->isPressEvent())
- deliverPressEvent(event, &hasFiltered);
- if (!event->allPointsAccepted())
- deliverUpdatedTouchPoints(event, &hasFiltered);
+ deliverPressOrReleaseEvent(event);
+ if (!event->allUpdatedPointsAccepted())
+ deliverUpdatedTouchPoints(event);
+ if (event->isReleaseEvent())
+ deliverPressOrReleaseEvent(event, true);
// Remove released points from itemForTouchPointId
bool allReleased = true;
@@ -2263,7 +2316,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event)
if (point->state() == QQuickEventPoint::Released) {
int id = point->pointId();
qCDebug(DBG_TOUCH_TARGET) << "TP" << hex << id << "released";
- point->setGrabber(nullptr);
+ point->setGrabberItem(nullptr);
if (id == touchMouseId) {
touchMouseId = -1;
touchMouseDevice = nullptr;
@@ -2273,29 +2326,92 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event)
}
}
- if (allReleased && !event->grabbers().isEmpty()) {
- qWarning() << "No release received for some grabbers" << event->grabbers();
+ if (allReleased && !event->exclusiveGrabbers().isEmpty()) {
+ qWarning() << "No release received for some grabbers" << event->exclusiveGrabbers();
event->clearGrabbers();
}
}
// Deliver touch points to existing grabbers
-bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered)
-{
- const auto grabbers = event->grabbers();
- for (auto grabber : grabbers)
- deliverMatchingPointsToItem(grabber, event, hasFiltered);
+void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event)
+{
+ const auto grabbers = event->exclusiveGrabbers();
+ for (auto grabber : grabbers) {
+ // The grabber is guaranteed to be either an item or a handler.
+ QQuickItem *receiver = qmlobject_cast<QQuickItem *>(grabber);
+ if (!receiver) {
+ // 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();
+ if (sendFilteredPointerEvent(event, receiver))
+ return;
+ event->localize(receiver);
+ handler->handlePointerEvent(event);
+ if (event->allPointsAccepted())
+ return;
+ }
+ // If the grabber is an item or the grabbing handler didn't handle it,
+ // then deliver the event to the item (which may have multiple handlers).
+ deliverMatchingPointsToItem(receiver, event);
+ }
- return false;
+ // If some points weren't grabbed, deliver only to non-grabber PointerHandlers
+ if (!event->allPointsGrabbed()) {
+ int pointCount = event->pointCount();
+
+ // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once)
+ const QVector<QQuickPointerHandler *> &eventDeliveryTargets = event->device()->eventDeliveryTargets();
+ for (int i = 0; i < pointCount; ++i) {
+ QQuickEventPoint *point = event->point(i);
+ for (auto handler : point->passiveGrabbers()) {
+ if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) {
+ if (sendFilteredPointerEvent(event, handler->parentItem()))
+ return;
+ handler->handlePointerEvent(event);
+ }
+ }
+ }
+
+ // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order
+ if (!event->allPointsGrabbed()) {
+ QVector<QQuickItem *> targetItems;
+ for (int i = 0; i < pointCount; ++i) {
+ 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->scenePos(), false, false);
+ if (targetItems.count()) {
+ targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
+ } else {
+ targetItems = targetItemsForPoint;
+ }
+ }
+
+ for (QQuickItem *item: targetItems) {
+ if (grabbers.contains(item))
+ continue;
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ event->localize(item);
+ itemPrivate->handlePointerEvent(event, true); // avoid re-delivering to grabbers
+ if (event->allPointsGrabbed())
+ break;
+ }
+ }
+ }
}
-// Deliver newly pressed touch points
-bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQuickItem *> *hasFiltered)
+// Deliver an event containing newly pressed or released touch points
+bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, bool handlersOnly)
{
- const QVector<QPointF> points = event->unacceptedPressedPointScenePositions();
+ int pointCount = event->pointCount();
QVector<QQuickItem *> targetItems;
- for (QPointF point: points) {
- QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, false);
+ bool isTouchEvent = (event->asPointerTouchEvent() != nullptr);
+ for (int i = 0; i < pointCount; ++i) {
+ auto point = event->point(i);
+ 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->scenePos(), !isTouchEvent, isTouchEvent);
if (targetItems.count()) {
targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
} else {
@@ -2303,8 +2419,18 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQui
}
}
+ if (allowChildEventFiltering && !handlersOnly) {
+ updateFilteringParentItems(targetItems);
+ QQuickItem *filteredItem;
+ if (sendFilteredPointerEvent(event, nullptr, &filteredItem)) {
+ if (event->isAccepted())
+ return true;
+ targetItems.removeAll(filteredItem);
+ }
+ }
+
for (QQuickItem *item: targetItems) {
- deliverMatchingPointsToItem(item, event, hasFiltered);
+ deliverMatchingPointsToItem(item, event, handlersOnly);
if (event->allPointsAccepted())
break;
}
@@ -2312,17 +2438,35 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQui
return event->allPointsAccepted();
}
-bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet<QQuickItem *> *hasFiltered)
+void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly)
{
Q_Q(QQuickWindow);
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ pointerEvent->localize(item);
+
+ // Let the Item's handlers (if any) have the event first.
+ // However, double click should never be delivered to handlers.
+ if (!pointerEvent->isDoubleClickEvent()) {
+ itemPrivate->handlePointerEvent(pointerEvent);
+ allowDoubleClick = !(pointerEvent->asPointerMouseEvent() && pointerEvent->isPressEvent() && pointerEvent->allPointsAccepted());
+ }
+ if (handlersOnly)
+ return;
+ if (pointerEvent->allPointsAccepted() && !pointerEvent->isReleaseEvent())
+ return;
+
+ // If all points are released and the item is not the grabber, it doesn't get the event.
+ // But if at least one point is still pressed, we might be in a potential gesture-takeover scenario.
+ if (pointerEvent->isReleaseEvent() && !pointerEvent->isUpdateEvent()
+ && !pointerEvent->exclusiveGrabbers().contains(item))
+ return;
// TODO: unite this mouse point delivery with the synthetic mouse event below
- if (auto event = pointerEvent->asPointerMouseEvent()) {
- if (item->acceptedMouseButtons() & event->button()) {
+ auto event = pointerEvent->asPointerMouseEvent();
+ if (event && item->acceptedMouseButtons() & event->button()) {
auto point = event->point(0);
if (point->isAccepted())
- return false;
-
+ return;
// The only reason to already have a mouse grabber here is
// synthetic events - flickable sends one when setPressDelay is used.
auto oldMouseGrabber = q->mouseGrabberItem();
@@ -2330,7 +2474,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
Q_ASSERT(item->contains(localPos)); // transform is checked already
QMouseEvent *me = event->asMouseEvent(localPos);
me->accept();
- q->sendEvent(item, me);
+ QCoreApplication::sendEvent(item, me);
if (me->isAccepted()) {
auto mouseGrabber = q->mouseGrabberItem();
if (mouseGrabber && mouseGrabber != item && mouseGrabber != oldMouseGrabber) {
@@ -2340,33 +2484,25 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
}
point->setAccepted(true);
}
- return me->isAccepted();
- }
- return false;
+ return;
}
- QQuickPointerTouchEvent *event = pointerEvent->asPointerTouchEvent();
- if (!event)
- return false;
+ QQuickPointerTouchEvent *ptEvent = pointerEvent->asPointerTouchEvent();
+ if (!ptEvent)
+ return;
- QScopedPointer<QTouchEvent> touchEvent(event->touchEventForItem(item));
+ QScopedPointer<QTouchEvent> touchEvent(ptEvent->touchEventForItem(item));
if (!touchEvent)
- return false;
+ return;
- qCDebug(DBG_TOUCH) << " - considering delivering " << touchEvent.data() << " to " << item;
+ qCDebug(DBG_TOUCH) << "considering delivering " << touchEvent.data() << " to " << item;
bool eventAccepted = false;
- // First check whether the parent wants to be a filter,
- // and if the parent accepts the event we are done.
- if (sendFilteredTouchEvent(item->parentItem(), item, event, hasFiltered)) {
- // If the touch was accepted (regardless by whom or in what form),
- // update acceptedNewPoints
- qCDebug(DBG_TOUCH) << " - can't. intercepted " << touchEvent.data() << " to " << item->parentItem() << " instead of " << item;
- for (auto point: qAsConst(touchEvent->touchPoints())) {
- event->pointById(point.id())->setAccepted();
- }
- return true;
- }
+ // If any parent filters the event, we're done.
+ // updateFilteringParentItems was called when the press occurred,
+ // and we assume that the filtering relationships don't change between press and release.
+ if (sendFilteredPointerEvent(pointerEvent, item))
+ return;
// Deliver the touch event to the given item
qCDebug(DBG_TOUCH) << " - actually delivering " << touchEvent.data() << " to " << item;
@@ -2374,36 +2510,34 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
eventAccepted = touchEvent->isAccepted();
// If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
// send mouse event
- if (deliverTouchAsMouse(item, event))
+ if (deliverTouchAsMouse(item, ptEvent))
eventAccepted = true;
}
if (eventAccepted) {
// If the touch was accepted (regardless by whom or in what form),
// update accepted new points.
+ bool isPressOrRelease = pointerEvent->isPressEvent() || pointerEvent->isReleaseEvent();
for (auto point: qAsConst(touchEvent->touchPoints())) {
- auto pointerEventPoint = event->pointById(point.id());
+ auto pointerEventPoint = ptEvent->pointById(point.id());
pointerEventPoint->setAccepted();
- if (point.state() == Qt::TouchPointPressed)
- pointerEventPoint->setGrabber(item);
+ if (isPressOrRelease)
+ pointerEventPoint->setGrabberItem(item);
}
} else {
// But if the event was not accepted then we know this item
// will not be interested in further updates for those touchpoint IDs either.
for (auto point: qAsConst(touchEvent->touchPoints())) {
if (point.state() == Qt::TouchPointPressed) {
- if (event->pointById(point.id())->grabber() == item) {
- qCDebug(DBG_TOUCH_TARGET) << "TP" << point.id() << "disassociated";
- event->pointById(point.id())->setGrabber(nullptr);
+ if (ptEvent->pointById(point.id())->exclusiveGrabber() == item) {
+ qCDebug(DBG_TOUCH_TARGET) << "TP" << hex << point.id() << "disassociated";
+ ptEvent->pointById(point.id())->setGrabberItem(nullptr);
}
}
}
}
-
- return eventAccepted;
}
#if QT_CONFIG(draganddrop)
@@ -2578,38 +2712,86 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF
}
#endif
-bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered)
+void QQuickWindowPrivate::updateFilteringParentItems(const QVector<QQuickItem *> &targetItems)
{
- Q_Q(QQuickWindow);
+ if (Q_UNLIKELY(DBG_MOUSE_TARGET().isDebugEnabled())) {
+ // qDebug() << map(&objectName, targetItems) but done the hard way because C++ is still that primitive
+ QStringList targetNames;
+ for (QQuickItem *t : targetItems)
+ targetNames << (QLatin1String(t->metaObject()->className()) + QLatin1Char(' ') + t->objectName());
+ qCDebug(DBG_MOUSE_TARGET) << "finding potential filtering parents of" << targetNames;
+ }
+ filteringParentItems.clear();
+ for (QQuickItem *item : targetItems) {
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ bool acceptsTouchEvents = item->acceptTouchEvents();
+#else
+ // In versions prior to Qt 6, we can't trust item->acceptTouchEvents() here, because it defaults to true.
+ bool acceptsTouchEvents = false;
+#endif
+ QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
+ // If the item neither handles events nor has handlers which do, then it will never be a receiver, so filtering is irrelevant.
+ if (!item->acceptedMouseButtons() && !acceptsTouchEvents &&
+ !(itemPriv->extra.isAllocated() && !itemPriv->extra->pointerHandlers.isEmpty()))
+ continue;
+ QQuickItem *parent = item->parentItem();
+ while (parent) {
+ if (parent->filtersChildMouseEvents()) {
+ bool foundParent = false;
+ for (const QPair<QQuickItem*,QQuickItem*> existingItemAndParent : filteringParentItems)
+ if (existingItemAndParent.second == parent)
+ foundParent = true;
+ if (!foundParent)
+ filteringParentItems.append(QPair<QQuickItem*,QQuickItem*>(item, parent));
+ }
+ parent = parent->parentItem();
+ }
+ }
+}
- if (!target)
+bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem **itemThatFiltered)
+{
+ if (!allowChildEventFiltering)
return false;
-
- bool filtered = false;
-
- QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
- if (targetPrivate->filtersChildMouseEvents && !hasFiltered->contains(target)) {
- hasFiltered->insert(target);
- QScopedPointer<QTouchEvent> targetEvent(event->touchEventForItem(target, true));
- if (targetEvent) {
- if (target->childMouseEventFilter(item, targetEvent.data())) {
- qCDebug(DBG_TOUCH) << " - first chance intercepted on childMouseEventFilter by " << target;
- QVector<int> touchIds;
- const int touchPointCount = targetEvent->touchPoints().size();
- touchIds.reserve(touchPointCount);
- for (int i = 0; i < touchPointCount; ++i)
- touchIds.append(targetEvent->touchPoints().at(i).id());
- target->grabTouchPoints(touchIds);
- if (q->mouseGrabberItem()) {
- q->mouseGrabberItem()->ungrabMouse();
- touchMouseId = -1;
- touchMouseDevice = nullptr;
- }
- filtered = true;
+ bool ret = false;
+ if (QQuickPointerMouseEvent *pme = event->asPointerMouseEvent()) {
+ for (QPair<QQuickItem *,QQuickItem *> itemAndParent : filteringParentItems) {
+ QQuickItem *item = receiver ? receiver : itemAndParent.first;
+ QQuickItem *filteringParent = itemAndParent.second;
+ if (item == filteringParent)
+ continue; // a filtering item never needs to filter for itself
+ QPointF localPos = item->mapFromScene(pme->point(0)->scenePos());
+ QMouseEvent *me = pme->asMouseEvent(localPos);
+ if (filteringParent->childMouseEventFilter(item, me)) {
+ if (itemThatFiltered) *itemThatFiltered = item;
+ ret = true;
}
-
- for (int i = 0; i < targetEvent->touchPoints().size(); ++i) {
- const QTouchEvent::TouchPoint &tp = targetEvent->touchPoints().at(i);
+ }
+ } else if (QQuickPointerTouchEvent *pte = event->asPointerTouchEvent()) {
+ QVarLengthArray<QPair<QQuickPointerHandler *, QQuickEventPoint *>, 32> passiveGrabsToCancel;
+ for (QPair<QQuickItem *,QQuickItem *> itemAndParent : filteringParentItems) {
+ QQuickItem *item = receiver ? receiver : itemAndParent.first;
+ if (item != itemAndParent.first)
+ continue;
+ if (!item->acceptTouchEvents() && !item->acceptedMouseButtons())
+ continue; // if this item won't accept, parents don't need to filter the touch for it
+ QQuickItem *filteringParent = itemAndParent.second;
+ if (item == filteringParent)
+ continue; // a filtering item never needs to filter for itself
+ // get a touch event customized for delivery to filteringParent
+ QScopedPointer<QTouchEvent> filteringParentTouchEvent(pte->touchEventForItem(filteringParent, true));
+ if (!filteringParentTouchEvent)
+ continue; // all points are stationary, or no touchpoints inside that parent
+ if (filteringParent->childMouseEventFilter(item, filteringParentTouchEvent.data())) {
+ qCDebug(DBG_TOUCH) << "touch event intercepted by childMouseEventFilter of " << filteringParent;
+ for (auto point: qAsConst(filteringParentTouchEvent->touchPoints()))
+ event->pointById(point.id())->setAccepted();
+ ret = true;
+ break;
+ }
+ // filteringParent didn't filter the touch event. Give it a chance to filter a synthetic mouse event.
+ for (int i = 0; i < filteringParentTouchEvent->touchPoints().size(); ++i) {
+ const QTouchEvent::TouchPoint &tp = filteringParentTouchEvent->touchPoints().at(i);
QEvent::Type t;
switch (tp.state()) {
@@ -2629,29 +2811,29 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem
bool touchMouseUnset = (touchMouseId == -1);
// Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId
if (touchMouseUnset || touchMouseId == tp.id()) {
- // targetEvent is already transformed wrt local position, velocity, etc.
-
- // FIXME: remove asTouchEvent!!!
- QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, event->asTouchEvent(), item, false));
+ // convert filteringParentTouchEvent (which is already transformed wrt local position, velocity, etc.)
+ // into a synthetic mouse event, and let childMouseEventFilter() have another chance with that
+ QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, filteringParentTouchEvent.data(), item, false));
// If a filtering item calls QQuickWindow::mouseGrabberItem(), it should
// report the touchpoint's grabber. Whenever we send a synthetic mouse event,
// touchMouseId and touchMouseDevice must be set, even if it's only temporarily and isn't grabbed.
touchMouseId = tp.id();
touchMouseDevice = event->device();
- if (target->childMouseEventFilter(item, mouseEvent.data())) {
- qCDebug(DBG_TOUCH) << " - second chance intercepted on childMouseEventFilter by " << target;
+ if (filteringParent->childMouseEventFilter(item, mouseEvent.data())) {
+ qCDebug(DBG_TOUCH) << "touch event intercepted as synth mouse event by childMouseEventFilter of " << filteringParent;
if (t != QEvent::MouseButtonRelease) {
- qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << target;
- if (touchMouseUnset) {
- // the point was grabbed as a pure touch point before, now it will be treated as mouse
- // but the old receiver still needs to be informed
- if (auto oldGrabber = pointerEventInstance(touchMouseDevice)->pointById(tp.id())->grabber())
- oldGrabber->touchUngrabEvent();
- }
+ qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << tp.id() << "->" << filteringParent;
+ pointerEventInstance(touchMouseDevice)->pointById(tp.id())->setGrabberItem(filteringParent);
touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set
- target->grabMouse();
+ filteringParent->grabMouse();
+ auto pointerEventPoint = pte->pointById(tp.id());
+ for (auto handler : pointerEventPoint->passiveGrabbers()) {
+ QPair<QQuickPointerHandler *, QQuickEventPoint *> grab(handler, pointerEventPoint);
+ if (!passiveGrabsToCancel.contains(grab))
+ passiveGrabsToCancel.append(grab);
+ }
}
- filtered = true;
+ ret = true;
}
if (touchMouseUnset) {
// Now that we're done sending a synth mouse event, and it wasn't grabbed,
@@ -2659,14 +2841,16 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem
touchMouseId = -1;
touchMouseDevice = nullptr;
}
- // Only one event can be filtered as a mouse event.
- break;
+ // Only one touchpoint can be treated as a synthetic mouse, so after childMouseEventFilter
+ // has been called once, we're done with this loop over the touchpoints.
+ break;
}
}
}
+ for (auto grab : passiveGrabsToCancel)
+ grab.second->cancelPassiveGrab(grab.first);
}
-
- return sendFilteredTouchEvent(target->parentItem(), item, event, hasFiltered) || filtered;
+ return ret;
}
bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event, QSet<QQuickItem *> *hasFiltered)
@@ -2798,6 +2982,8 @@ void QQuickWindowPrivate::contextCreationFailureMessage(const QSurfaceFormat &fo
#endif // !Q_OS_WIN32
}
+#if QT_DEPRECATED_SINCE(5, 8)
+
/*!
Propagates an event \a e to a QQuickItem \a item on the window.
@@ -2850,6 +3036,8 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e)
return false;
}
+#endif
+
void QQuickWindowPrivate::cleanupNodes()
{
for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
@@ -3451,6 +3639,11 @@ void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo)
The specified FBO must be created in the context of the window
or one that shares with it.
+ \note \a fboId can also be set to 0. In this case rendering will target the
+ default framebuffer of whichever surface is current when the scenegraph
+ renders. \a size must still be valid, specifying the dimensions of the
+ surface.
+
\note
This function only has an effect when using the default OpenGL scene
graph adaptation.
diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h
index 9c3e7277bc..d1a16fbeb6 100644
--- a/src/quick/items/qquickwindow.h
+++ b/src/quick/items/qquickwindow.h
@@ -112,7 +112,9 @@ public:
QQuickItem *mouseGrabberItem() const;
- bool sendEvent(QQuickItem *, QEvent *);
+#if QT_DEPRECATED_SINCE(5, 8)
+ QT_DEPRECATED bool sendEvent(QQuickItem *, QEvent *);
+#endif
QImage grabWindow();
#if QT_CONFIG(opengl)
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index b3ff5a2b35..14564a7f55 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -143,11 +143,12 @@ public:
bool deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent);
void translateTouchEvent(QTouchEvent *touchEvent);
void setMouseGrabber(QQuickItem *grabber);
- void grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids);
+ void grabTouchPoints(QObject *grabber, const QVector<int> &ids);
void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true);
static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0);
void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent);
bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet<QQuickItem *> *);
+ bool sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem **itemThatFiltered = 0);
#if QT_CONFIG(wheelevent)
bool deliverWheelEvent(QQuickItem *, QWheelEvent *);
#endif
@@ -171,13 +172,13 @@ public:
void deliverPointerEvent(QQuickPointerEvent *);
void deliverTouchEvent(QQuickPointerTouchEvent *);
bool deliverTouchCancelEvent(QTouchEvent *);
- bool deliverPressEvent(QQuickPointerEvent *, QSet<QQuickItem *> *);
- bool deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered);
- bool deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet<QQuickItem*> *filtered);
- bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet<QQuickItem*> *filtered);
+ bool deliverPressOrReleaseEvent(QQuickPointerEvent *, bool handlersOnly = false);
+ void deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event);
+ void deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly = false);
- QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons) const;
+ QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons, bool checkAcceptsTouch) const;
QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const;
+ void updateFilteringParentItems(const QVector<QQuickItem *> &targetItems);
// hover delivery
bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted);
@@ -226,6 +227,7 @@ public:
QList<QSGNode *> cleanupNodeList;
QVector<QQuickItem *> itemsToPolish;
+ QVector<QPair<QQuickItem *,QQuickItem *> > filteringParentItems; // item:parent pairs
qreal devicePixelRatio;
QMetaObject::Connection physicalDpiChangedConnection;
@@ -263,6 +265,9 @@ public:
uint lastWheelEventAccepted : 1;
bool componentCompleted : 1;
+ bool allowChildEventFiltering : 1;
+ bool allowDoubleClick : 1;
+
Qt::FocusReason lastFocusReason;
QOpenGLFramebufferObject *renderTarget;
@@ -290,6 +295,14 @@ public:
return overThreshold;
}
+ static bool dragOverThreshold(const QQuickEventPoint *point)
+ {
+ QPointF delta = point->scenePos() - point->scenePressPos();
+ return (QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point) ||
+ QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point));
+ }
+
+
// data property
static void data_append(QQmlListProperty<QObject> *, QObject *);
static int data_count(QQmlListProperty<QObject> *);
diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp
index c1cc02568c..45e3f0004d 100644
--- a/src/quick/items/qquickwindowmodule.cpp
+++ b/src/quick/items/qquickwindowmodule.cpp
@@ -200,6 +200,7 @@ void QQuickWindowModule::defineModule()
qmlRegisterUncreatableType<QQuickScreen>(uri, 2, 0, "Screen", QStringLiteral("Screen can only be used via the attached property."));
qmlRegisterUncreatableType<QQuickScreen,1>(uri, 2, 3, "Screen", QStringLiteral("Screen can only be used via the attached property."));
qmlRegisterUncreatableType<QQuickScreenInfo,2>(uri, 2, 3, "ScreenInfo", QStringLiteral("ScreenInfo can only be used via the attached property."));
+ qmlRegisterUncreatableType<QQuickScreenInfo,10>(uri, 2, 10, "ScreenInfo", QStringLiteral("ScreenInfo can only be used via the attached property."));
}
QT_END_NAMESPACE
diff --git a/src/quick/quick.pro b/src/quick/quick.pro
index eae9b09b2f..e9a8b84b2a 100644
--- a/src/quick/quick.pro
+++ b/src/quick/quick.pro
@@ -30,6 +30,7 @@ ANDROID_BUNDLED_FILES += \
include(util/util.pri)
include(scenegraph/scenegraph.pri)
include(items/items.pri)
+include(handlers/handlers.pri)
qtConfig(quick-designer): \
include(designer/designer.pri)
qtConfig(accessibility) {
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h
index d3f13e40b1..9f5a22e66f 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h
@@ -93,6 +93,7 @@ public:
void setDevicePixelRatio(qreal ratio) override;
void setMirrorHorizontal(bool mirror) override;
void setMirrorVertical(bool mirror) override;
+ void setSamples(int) override { }
public slots:
void markDirtyTexture() override;
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp
index f8c1a3d90b..ad6cf39425 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp
@@ -64,7 +64,7 @@ void QSGSoftwarePixmapRenderer::renderScene(uint)
class B : public QSGBindable
{
public:
- void bind() const { }
+ void bind() const override { }
} bindable;
QSGRenderer::renderScene(bindable);
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
index 384c124a02..77d21ec042 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
@@ -101,10 +101,10 @@ void QSGSoftwareImageNode::paint(QPainter *painter)
if (!m_cachedPixmap.isNull()) {
painter->drawPixmap(m_rect, m_cachedPixmap, m_sourceRect);
- } else if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(m_texture)) {
+ } else if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture *>(m_texture)) {
const QPixmap &pm = pt->pixmap();
painter->drawPixmap(m_rect, pm, m_sourceRect);
- } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(m_texture)) {
+ } else if (QSGPlainTexture *pt = qobject_cast<QSGPlainTexture *>(m_texture)) {
const QImage &im = pt->image();
painter->drawImage(m_rect, im, m_sourceRect);
}
@@ -116,14 +116,14 @@ void QSGSoftwareImageNode::updateCachedMirroredPixmap()
m_cachedPixmap = QPixmap();
} else {
- if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(m_texture)) {
+ if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture *>(m_texture)) {
QTransform mirrorTransform;
if (m_transformMode.testFlag(MirrorVertically))
mirrorTransform = mirrorTransform.scale(1, -1);
if (m_transformMode.testFlag(MirrorHorizontally))
mirrorTransform = mirrorTransform.scale(-1, 1);
m_cachedPixmap = pt->pixmap().transformed(mirrorTransform);
- } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(m_texture)) {
+ } else if (QSGPlainTexture *pt = qobject_cast<QSGPlainTexture *>(m_texture)) {
m_cachedPixmap = QPixmap::fromImage(pt->image().mirrored(m_transformMode.testFlag(MirrorHorizontally), m_transformMode.testFlag(MirrorVertically)));
} else {
m_cachedPixmap = QPixmap();
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
index e5b9430236..cecc6c21ca 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
@@ -277,18 +277,21 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu
QMatrix4x4 m = m_transform;
rd->m_matrix = &m;
rd->m_opacity = m_opacity;
- RenderNodeState rs;
- rs.cr = m_clipRegion;
- const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering)
- ? m_boundingRectMax :
- QRect(0, 0, painter->device()->width(), painter->device()->height());
+ // all the clip region below is in world coordinates, taking m_transform into account already
+ QRegion cr = m_dirtyRegion;
+ if (m_clipRegion.rectCount() > 1)
+ cr &= m_clipRegion;
painter->save();
- painter->setClipRegion(br, Qt::ReplaceClip);
+ RenderNodeState rs;
+ rs.cr = cr;
m_handle.renderNode->render(&rs);
painter->restore();
+ const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering)
+ ? m_boundingRectMax // already mapped to world
+ : QRect(0, 0, painter->device()->width(), painter->device()->height());
m_previousDirtyRegion = QRegion(br);
m_isDirty = false;
m_dirtyRegion = QRegion();
@@ -299,7 +302,7 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu
painter->save();
painter->setOpacity(m_opacity);
- // Set clipRegion to m_dirtyRegion (in world coordinates)
+ // Set clipRegion to m_dirtyRegion (in world coordinates, so must be done before the setTransform below)
// as m_dirtyRegion already accounts for clipRegion
painter->setClipRegion(m_dirtyRegion, Qt::ReplaceClip);
if (m_clipRegion.rectCount() > 1)
@@ -316,10 +319,10 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu
case QSGSoftwareRenderableNode::SimpleTexture:
{
QSGTexture *texture = m_handle.simpleTextureNode->texture();
- if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(texture)) {
+ if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture *>(texture)) {
const QPixmap &pm = pt->pixmap();
painter->drawPixmap(m_handle.simpleTextureNode->rect(), pm, m_handle.simpleTextureNode->sourceRect());
- } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(texture)) {
+ } else if (QSGPlainTexture *pt = qobject_cast<QSGPlainTexture *>(texture)) {
const QImage &im = pt->image();
painter->drawImage(m_handle.simpleTextureNode->rect(), im, m_handle.simpleTextureNode->sourceRect());
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
index cad826fb27..85d04fe136 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
@@ -90,7 +90,7 @@ void QSGSoftwareRenderer::renderScene(uint)
class B : public QSGBindable
{
public:
- void bind() const { }
+ void bind() const override { }
} bindable;
QSGRenderer::renderScene(bindable);
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
index 19f16a79fb..832b69d0cc 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
@@ -188,8 +188,8 @@ public:
delete rc;
}
- bool event(QEvent *e);
- void run();
+ bool event(QEvent *e) override;
+ void run() override;
void syncAndRender();
void sync(bool inExpose);
diff --git a/src/quick/scenegraph/compressedtexture/qsgpkmhandler.cpp b/src/quick/scenegraph/compressedtexture/qsgpkmhandler.cpp
new file mode 100644
index 0000000000..1b8882e9a5
--- /dev/null
+++ b/src/quick/scenegraph/compressedtexture/qsgpkmhandler.cpp
@@ -0,0 +1,209 @@
+/****************************************************************************
+**
+** 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 "qsgpkmhandler_p.h"
+
+#include <QFile>
+#include <QDebug>
+#include <qendian.h>
+#include <qopenglfunctions.h>
+#include <qqmlfile.h>
+
+//#define ETC_DEBUG
+
+#ifndef GL_ETC1_RGB8_OES
+ #define GL_ETC1_RGB8_OES 0x8d64
+#endif
+
+#ifndef GL_COMPRESSED_RGB8_ETC2
+ #define GL_COMPRESSED_RGB8_ETC2 0x9274
+#endif
+
+#ifndef GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2
+ #define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276
+#endif
+
+#ifndef GL_COMPRESSED_RGBA8_ETC2_EAC
+ #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278
+#endif
+
+QT_BEGIN_NAMESPACE
+
+static const int headerSize = 16;
+
+static unsigned int typeMap[5] = {
+ GL_ETC1_RGB8_OES,
+ GL_COMPRESSED_RGB8_ETC2,
+ 0, // unused
+ GL_COMPRESSED_RGBA8_ETC2_EAC,
+ GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2
+};
+
+EtcTexture::EtcTexture()
+ : m_texture_id(0), m_uploaded(false)
+{
+ initializeOpenGLFunctions();
+}
+
+EtcTexture::~EtcTexture()
+{
+ if (m_texture_id)
+ glDeleteTextures(1, &m_texture_id);
+}
+
+int EtcTexture::textureId() const
+{
+ if (m_texture_id == 0) {
+ EtcTexture *texture = const_cast<EtcTexture*>(this);
+ texture->glGenTextures(1, &texture->m_texture_id);
+ }
+ return m_texture_id;
+}
+
+bool EtcTexture::hasAlphaChannel() const
+{
+ return m_type == GL_COMPRESSED_RGBA8_ETC2_EAC ||
+ m_type == GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;
+}
+
+
+void EtcTexture::bind()
+{
+ if (m_uploaded && m_texture_id) {
+ glBindTexture(GL_TEXTURE_2D, m_texture_id);
+ return;
+ }
+
+ if (m_texture_id == 0)
+ glGenTextures(1, &m_texture_id);
+ glBindTexture(GL_TEXTURE_2D, m_texture_id);
+
+#ifdef ETC_DEBUG
+ qDebug() << "glCompressedTexImage2D, width: " << m_size.width() << "height" << m_size.height() <<
+ "paddedWidth: " << m_paddedSize.width() << "paddedHeight: " << m_paddedSize.height();
+#endif
+
+#ifndef QT_NO_DEBUG
+ while (glGetError() != GL_NO_ERROR) { }
+#endif
+
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ Q_ASSERT(ctx != 0);
+ ctx->functions()->glCompressedTexImage2D(GL_TEXTURE_2D, 0, m_type,
+ m_size.width(), m_size.height(), 0,
+ (m_paddedSize.width() * m_paddedSize.height()) / 2,
+ m_data.data() + headerSize);
+
+#ifndef QT_NO_DEBUG
+ // Gracefully fail in case of an error...
+ GLuint error = glGetError();
+ if (error != GL_NO_ERROR) {
+ qDebug () << "glCompressedTexImage2D for compressed texture failed, error: " << error;
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDeleteTextures(1, &m_texture_id);
+ m_texture_id = 0;
+ return;
+ }
+#endif
+
+ m_uploaded = true;
+ updateBindOptions(true);
+}
+
+class QEtcTextureFactory : public QQuickTextureFactory
+{
+public:
+ QByteArray m_data;
+ QSize m_size;
+ QSize m_paddedSize;
+ unsigned int m_type;
+
+ QSize textureSize() const { return m_size; }
+ int textureByteCount() const { return m_data.size(); }
+
+ QSGTexture *createTexture(QQuickWindow *) const {
+ EtcTexture *texture = new EtcTexture;
+ texture->m_data = m_data;
+ texture->m_size = m_size;
+ texture->m_paddedSize = m_paddedSize;
+ texture->m_type = m_type;
+ return texture;
+ }
+};
+
+QQuickTextureFactory *QSGPkmHandler::read(QIODevice *device)
+{
+ QScopedPointer<QEtcTextureFactory> ret(new QEtcTextureFactory);
+ ret->m_data = device->readAll();
+ if (ret->m_data.isEmpty() || ret->m_data.size() < headerSize)
+ return nullptr;
+
+ const char *rawData = ret->m_data.constData();
+
+ // magic number
+ if (qstrncmp(rawData, "PKM ", 4) != 0)
+ return nullptr;
+
+ // currently ignore version (rawData + 4)
+
+ // texture type
+ quint16 type = qFromBigEndian<quint16>(rawData + 6);
+ static int typeCount = sizeof(typeMap)/sizeof(typeMap[0]);
+ if (type >= typeCount)
+ return nullptr;
+ ret->m_type = typeMap[type];
+
+ // texture size
+ ret->m_paddedSize.setWidth(qFromBigEndian<quint16>(rawData + 8));
+ ret->m_paddedSize.setHeight(qFromBigEndian<quint16>(rawData + 10));
+ if ((ret->m_paddedSize.width() * ret->m_paddedSize.height()) / 2 > ret->m_data.size() - headerSize)
+ return nullptr;
+ ret->m_size.setWidth(qFromBigEndian<quint16>(rawData + 12));
+ ret->m_size.setHeight(qFromBigEndian<quint16>(rawData + 14));
+ if (ret->m_size.isEmpty())
+ return nullptr;
+
+#ifdef ETC_DEBUG
+ qDebug() << "requestTexture returning: " << ret->m_data.length() << "bytes; width: " << ret->m_size.width() << ", height: " << ret->m_size.height();
+#endif
+
+ return ret.take();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/compressedtexture/qsgpkmhandler_p.h b/src/quick/scenegraph/compressedtexture/qsgpkmhandler_p.h
new file mode 100644
index 0000000000..77097cb80a
--- /dev/null
+++ b/src/quick/scenegraph/compressedtexture/qsgpkmhandler_p.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** 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 QSGPKMHANDLER_H
+#define QSGPKMHANDLER_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 <QOpenGLFunctions>
+#include <QQuickImageProvider>
+#include <QtQuick/QSGTexture>
+#include <QUrl>
+
+QT_BEGIN_NAMESPACE
+
+class QSGPkmHandler
+{
+public:
+ QSGPkmHandler() {}
+
+ QQuickTextureFactory *read(QIODevice *device);
+};
+
+class EtcTexture : public QSGTexture, protected QOpenGLFunctions
+{
+ Q_OBJECT
+public:
+ EtcTexture();
+ ~EtcTexture();
+
+ void bind();
+
+ QSize textureSize() const { return m_size; }
+ int textureId() const;
+
+ bool hasAlphaChannel() const;
+ bool hasMipmaps() const { return false; }
+
+ QByteArray m_data;
+ QSize m_size;
+ QSize m_paddedSize;
+ GLuint m_texture_id;
+ GLenum m_type;
+ bool m_uploaded;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGPKMHANDLER_H
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
index 0458e2dead..8fbd402a2f 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
@@ -134,6 +134,7 @@ QSGRenderer::QSGRenderer(QSGRenderContext *context)
, m_bindable(0)
, m_changed_emitted(false)
, m_is_rendering(false)
+ , m_is_preprocessing(false)
{
}
@@ -189,7 +190,7 @@ void QSGRenderer::renderScene(uint fboId)
class B : public QSGBindable
{
public:
- void bind() const { QOpenGLFramebufferObject::bindDefault(); }
+ void bind() const override { QOpenGLFramebufferObject::bindDefault(); }
} bindable;
renderScene(bindable);
}
@@ -287,6 +288,8 @@ void QSGRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
void QSGRenderer::preprocess()
{
+ m_is_preprocessing = true;
+
QSGRootNode *root = rootNode();
Q_ASSERT(root);
@@ -298,6 +301,11 @@ void QSGRenderer::preprocess()
for (QSet<QSGNode *>::const_iterator it = items.constBegin();
it != items.constEnd(); ++it) {
QSGNode *n = *it;
+
+ // If we are currently preprocessing, check this node hasn't been
+ // deleted or something. we don't want a use-after-free!
+ if (m_nodes_dont_preprocess.contains(n)) // skip
+ continue;
if (!nodeUpdater()->isNodeBlocked(n, root)) {
n->preprocess();
}
@@ -315,8 +323,13 @@ void QSGRenderer::preprocess()
updatePassTime = frameTimer.nsecsElapsed();
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRendererFrame,
QQuickProfiler::SceneGraphRendererUpdate);
+
+ m_is_preprocessing = false;
+ m_nodes_dont_preprocess.clear();
}
+
+
void QSGRenderer::addNodesToPreprocess(QSGNode *node)
{
for (QSGNode *c = node->firstChild(); c; c = c->nextSibling())
@@ -329,8 +342,13 @@ void QSGRenderer::removeNodesToPreprocess(QSGNode *node)
{
for (QSGNode *c = node->firstChild(); c; c = c->nextSibling())
removeNodesToPreprocess(c);
- if (node->flags() & QSGNode::UsePreprocess)
+ if (node->flags() & QSGNode::UsePreprocess) {
m_nodes_to_preprocess.remove(node);
+
+ // If preprocessing *now*, mark the node as gone.
+ if (m_is_preprocessing)
+ m_nodes_dont_preprocess.insert(node);
+ }
}
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
index 4589685765..1ea2775e6f 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
@@ -118,11 +118,13 @@ private:
QSGNodeUpdater *m_node_updater;
QSet<QSGNode *> m_nodes_to_preprocess;
+ QSet<QSGNode *> m_nodes_dont_preprocess;
const QSGBindable *m_bindable;
uint m_changed_emitted : 1;
uint m_is_rendering : 1;
+ uint m_is_preprocessing : 1;
};
class Q_QUICK_PRIVATE_EXPORT QSGBindable
diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp
index 1bc0210b72..a8954848d6 100644
--- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp
@@ -267,6 +267,10 @@ QSGRenderNode::RenderingFlags QSGRenderNode::flags() const
For rendernodes covering the entire area of a corresponding QQuickItem the
return value will be (0, 0, item->width(), item->height()).
+ \note Nodes are also free to render outside the boundaries specified by the
+ item's width and height, since the scenegraph nodes are not bounded by the
+ QQuickItem geometry, as long as this is reported correctly from this function.
+
\sa flags()
*/
QRectF QSGRenderNode::rect() const
@@ -359,7 +363,10 @@ QSGRenderNode::RenderState::~RenderState()
of the render state is not in use. However, the clip region that can be set
on the QPainter still has to be communicated since reconstructing this
manually in render() is not reasonable. It can therefore be queried via
- this function.
+ this function. The region is in world coordinates and can be passed
+ to QPainter::setClipRegion() with Qt::ReplaceClip. This must be done before
+ calling QPainter::setTransform() since the clip region is already mapped to
+ the transform provided in QSGRenderNode::matrix().
*/
/*!
diff --git a/src/quick/scenegraph/coreapi/qsgshaderrewriter.cpp b/src/quick/scenegraph/coreapi/qsgshaderrewriter.cpp
index 2b70139b37..1fdc1720f7 100644
--- a/src/quick/scenegraph/coreapi/qsgshaderrewriter.cpp
+++ b/src/quick/scenegraph/coreapi/qsgshaderrewriter.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include <QtCore/QByteArray>
+#include <QtCore/QString>
#include <QtGui/QSurfaceFormat>
// Duct Tape tokenizer for the purpose of parsing and rewriting
diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp
index 0f7e555aa7..c64360f955 100644
--- a/src/quick/scenegraph/qsgadaptationlayer.cpp
+++ b/src/quick/scenegraph/qsgadaptationlayer.cpp
@@ -40,7 +40,6 @@
#include "qsgadaptationlayer_p.h"
#include <qmath.h>
-#include <QtQuick/private/qsgdistancefieldutil_p.h>
#include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qrawfont_p.h>
@@ -57,9 +56,8 @@ static QElapsedTimer qsg_render_timer;
QSGDistanceFieldGlyphCache::Texture QSGDistanceFieldGlyphCache::s_emptyTexture;
-QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font)
- : m_manager(man)
- , m_pendingGlyphs(64)
+QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font)
+ : m_pendingGlyphs(64)
{
Q_ASSERT(font.isValid());
diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h
index 03a1f7f281..ba146b884f 100644
--- a/src/quick/scenegraph/qsgadaptationlayer_p.h
+++ b/src/quick/scenegraph/qsgadaptationlayer_p.h
@@ -73,7 +73,6 @@ QT_BEGIN_NAMESPACE
class QSGNode;
class QImage;
class TextureReference;
-class QSGDistanceFieldGlyphCacheManager;
class QSGDistanceFieldGlyphNode;
class QOpenGLContext;
class QSGInternalImageNode;
@@ -209,6 +208,7 @@ public:
virtual void setDevicePixelRatio(qreal ratio) = 0;
virtual void setMirrorHorizontal(bool mirror) = 0;
virtual void setMirrorVertical(bool mirror) = 0;
+ virtual void setSamples(int samples) = 0;
Q_SLOT virtual void markDirtyTexture() = 0;
Q_SLOT virtual void invalidated() = 0;
@@ -408,7 +408,7 @@ public:
class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldGlyphCache
{
public:
- QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font);
+ QSGDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font);
virtual ~QSGDistanceFieldGlyphCache();
struct Metrics {
@@ -442,8 +442,6 @@ public:
bool operator == (const Texture &other) const { return textureId == other.textureId; }
};
- const QSGDistanceFieldGlyphCacheManager *manager() const { return m_manager; }
-
const QRawFont &referenceFont() const { return m_referenceFont; }
qreal fontScale(qreal pixelSize) const
@@ -513,8 +511,6 @@ protected:
inline bool isCoreProfile() const { return m_coreProfile; }
private:
- QSGDistanceFieldGlyphCacheManager *m_manager;
-
QRawFont m_referenceFont;
int m_glyphCount;
diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp
index c0a054a539..d460794573 100644
--- a/src/quick/scenegraph/qsgcontext.cpp
+++ b/src/quick/scenegraph/qsgcontext.cpp
@@ -228,14 +228,6 @@ public:
int m_good;
};
-class QSGTextureCleanupEvent : public QEvent
-{
-public:
- QSGTextureCleanupEvent(QSGTexture *t) : QEvent(QEvent::User), texture(t) { }
- ~QSGTextureCleanupEvent() { delete texture; }
- QSGTexture *texture;
-};
-
/*!
\class QSGContext
@@ -333,7 +325,6 @@ QSGRendererInterface *QSGContext::rendererInterface(QSGRenderContext *renderCont
QSGRenderContext::QSGRenderContext(QSGContext *context)
: m_sg(context)
- , m_distanceFieldCacheManager(0)
{
}
diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h
index 1c4cd0ce90..84a2523f26 100644
--- a/src/quick/scenegraph/qsgcontext_p.h
+++ b/src/quick/scenegraph/qsgcontext_p.h
@@ -78,7 +78,6 @@ class QSGMaterial;
class QSGRenderLoop;
class QSGLayer;
class QQuickTextureFactory;
-class QSGDistanceFieldGlyphCacheManager;
class QSGContext;
class QQuickPaintedItem;
class QSGRendererInterface;
@@ -195,7 +194,7 @@ protected:
QMutex m_mutex;
QHash<QQuickTextureFactory *, QSGTexture *> m_textures;
QSet<QSGTexture *> m_texturesToDelete;
- QSGDistanceFieldGlyphCacheManager *m_distanceFieldCacheManager;
+ QHash<QString, QSGDistanceFieldGlyphCache *> m_glyphCaches;
QSet<QFontEngine *> m_fontEnginesToClean;
};
diff --git a/src/quick/scenegraph/qsgcontextplugin.cpp b/src/quick/scenegraph/qsgcontextplugin.cpp
index b8b5141957..bd311d3b46 100644
--- a/src/quick/scenegraph/qsgcontextplugin.cpp
+++ b/src/quick/scenegraph/qsgcontextplugin.cpp
@@ -49,6 +49,9 @@
#include <QtQuick/private/qsgdefaultcontext_p.h>
#endif
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatformintegration.h>
+
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_INFO)
@@ -128,12 +131,12 @@ QSGAdaptationBackendData *contextFactory()
if (requestedBackend.isEmpty() && qEnvironmentVariableIsSet("QT_QUICK_BACKEND"))
requestedBackend = QString::fromLocal8Bit(qgetenv("QT_QUICK_BACKEND"));
-#if !QT_CONFIG(opengl)
- // If this is a build without OpenGL, and no backend has been set
+ // If this platform does not support OpenGL, and no backend has been set
// default to the software renderer
- if (requestedBackend.isEmpty())
+ if (requestedBackend.isEmpty()
+ && !QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) {
requestedBackend = QString::fromLocal8Bit("software");
-#endif
+ }
if (!requestedBackend.isEmpty()) {
qCDebug(QSG_LOG_INFO) << "Loading backend" << requestedBackend;
diff --git a/src/quick/scenegraph/qsgdefaultcontext.cpp b/src/quick/scenegraph/qsgdefaultcontext.cpp
index 405f1d86a4..be5fec9dab 100644
--- a/src/quick/scenegraph/qsgdefaultcontext.cpp
+++ b/src/quick/scenegraph/qsgdefaultcontext.cpp
@@ -39,7 +39,6 @@
#include "qsgdefaultcontext_p.h"
-#include <QtQuick/private/qsgdistancefieldutil_p.h>
#include <QtQuick/private/qsgdefaultinternalrectanglenode_p.h>
#include <QtQuick/private/qsgdefaultinternalimagenode_p.h>
#include <QtQuick/private/qsgdefaultpainternode_p.h>
@@ -68,13 +67,13 @@ QT_BEGIN_NAMESPACE
namespace QSGMultisampleAntialiasing {
class ImageNode : public QSGDefaultInternalImageNode {
public:
- void setAntialiasing(bool) { }
+ void setAntialiasing(bool) override { }
};
class RectangleNode : public QSGDefaultInternalRectangleNode {
public:
- void setAntialiasing(bool) { }
+ void setAntialiasing(bool) override { }
};
}
diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
index f0a336e229..7789ef8fb1 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
@@ -42,7 +42,6 @@
#include <QtGui/private/qdistancefield_p.h>
#include <QtGui/private/qopenglcontext_p.h>
#include <QtQml/private/qqmlglobal_p.h>
-#include <QtQuick/private/qsgdistancefieldutil_p.h>
#include <qopenglfunctions.h>
#include <qopenglframebufferobject.h>
#include <qmath.h>
@@ -60,8 +59,8 @@ DEFINE_BOOL_CONFIG_OPTION(qsgPreferFullSizeGlyphCacheTextures, QSG_PREFER_FULLSI
# define QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING 2
#endif
-QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font)
- : QSGDistanceFieldGlyphCache(man, c, font)
+QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font)
+ : QSGDistanceFieldGlyphCache(c, font)
, m_maxTextureSize(0)
, m_maxTextureCount(3)
, m_blitProgram(0)
@@ -72,12 +71,15 @@ QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QSGDistance
, m_coreFuncs(0)
#endif
{
- m_blitBuffer.create();
- m_blitBuffer.bind();
- static GLfloat buffer[16] = {-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f,
- 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
- m_blitBuffer.allocate(buffer, sizeof(buffer));
- m_blitBuffer.release();
+ if (Q_LIKELY(m_blitBuffer.create())) {
+ m_blitBuffer.bind();
+ static const GLfloat buffer[16] = {-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
+ m_blitBuffer.allocate(buffer, sizeof(buffer));
+ m_blitBuffer.release();
+ } else {
+ qWarning("Buffer creation failed");
+ }
m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize()));
}
diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
index 57dc4a5d07..fe365495c2 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
+++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
@@ -69,7 +69,7 @@ class QOpenGLFunctions_3_2_Core;
class Q_QUICK_PRIVATE_EXPORT QSGDefaultDistanceFieldGlyphCache : public QSGDistanceFieldGlyphCache
{
public:
- QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font);
+ QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font);
virtual ~QSGDefaultDistanceFieldGlyphCache();
void requestGlyphs(const QSet<glyph_t> &glyphs) override;
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
index b001899915..edb6e92a0d 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
@@ -91,11 +91,11 @@ class QSGTextMaskShader : public QSGMaterialShader
public:
QSGTextMaskShader(QFontEngine::GlyphFormat glyphFormat);
- virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
- virtual char const *const *attributeNames() const;
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
+ char const *const *attributeNames() const override;
protected:
- virtual void initialize();
+ void initialize() override;
int m_matrix_id;
int m_color_id;
@@ -181,7 +181,7 @@ public:
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/8bittextmask.frag"));
}
- virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
};
void QSG8BitTextMaskShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
@@ -206,10 +206,10 @@ public:
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/24bittextmask.frag"));
}
- virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
- virtual void initialize();
- void activate();
- void deactivate();
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
+ void initialize() override;
+ void activate() override;
+ void deactivate() override;
bool useSRGB() const;
uint m_useSRGB : 1;
@@ -326,10 +326,10 @@ public:
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/styledtext.frag"));
}
- virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
private:
- virtual void initialize();
+ void initialize() override;
int m_shift_id;
int m_styleColor_id;
diff --git a/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp
index 1d54628acd..a5a6da06a7 100644
--- a/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp
@@ -50,11 +50,11 @@ class SmoothTextureMaterialShader : public QSGTextureMaterialShader
public:
SmoothTextureMaterialShader();
- virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
- virtual char const *const *attributeNames() const;
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
+ char const *const *attributeNames() const override;
protected:
- virtual void initialize();
+ void initialize() override;
int m_pixelSizeLoc;
};
diff --git a/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp
index 94414444ba..e52dcaad52 100644
--- a/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp
@@ -55,11 +55,11 @@ class SmoothColorMaterialShader : public QSGMaterialShader
public:
SmoothColorMaterialShader();
- virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
- virtual char const *const *attributeNames() const;
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
+ char const *const *attributeNames() const override;
private:
- virtual void initialize();
+ void initialize() override;
int m_matrixLoc;
int m_opacityLoc;
diff --git a/src/quick/scenegraph/qsgdefaultlayer.cpp b/src/quick/scenegraph/qsgdefaultlayer.cpp
index 2897f53e32..86d74acf54 100644
--- a/src/quick/scenegraph/qsgdefaultlayer.cpp
+++ b/src/quick/scenegraph/qsgdefaultlayer.cpp
@@ -100,6 +100,7 @@ QSGDefaultLayer::QSGDefaultLayer(QSGRenderContext *context)
#ifdef QSG_DEBUG_FBO_OVERLAY
, m_debugOverlay(0)
#endif
+ , m_samples(0)
, m_mipmap(false)
, m_live(true)
, m_recursive(false)
@@ -314,11 +315,20 @@ void QSGDefaultLayer::grab()
QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
bool deleteFboLater = false;
- if (!m_fbo || m_fbo->size() != m_size || m_fbo->format().internalTextureFormat() != m_format
- || (!m_fbo->format().mipmap() && m_mipmap))
- {
+
+ int effectiveSamples = m_samples;
+ // By default m_samples is 0. Fall back to the context's setting in this case.
+ if (effectiveSamples == 0)
+ effectiveSamples = m_context->openglContext()->format().samples();
+
+ const bool needsNewFbo = !m_fbo || m_fbo->size() != m_size || m_fbo->format().internalTextureFormat() != m_format;
+ const bool mipmapGotEnabled = m_fbo && !m_fbo->format().mipmap() && m_mipmap;
+ const bool msaaGotEnabled = effectiveSamples > 1 && (!m_secondaryFbo || m_secondaryFbo->format().samples() != effectiveSamples);
+ const bool msaaGotDisabled = effectiveSamples <= 1 && m_secondaryFbo;
+
+ if (needsNewFbo || mipmapGotEnabled || msaaGotEnabled || msaaGotDisabled) {
if (!m_multisamplingChecked) {
- if (m_context->openglContext()->format().samples() <= 1) {
+ if (effectiveSamples <= 1) {
m_multisampling = false;
} else {
QOpenGLExtensions *e = static_cast<QOpenGLExtensions *>(funcs);
@@ -334,7 +344,7 @@ void QSGDefaultLayer::grab()
QOpenGLFramebufferObjectFormat format;
format.setInternalTextureFormat(m_format);
- format.setSamples(m_context->openglContext()->format().samples());
+ format.setSamples(effectiveSamples);
m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format);
m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_secondaryFbo);
} else {
diff --git a/src/quick/scenegraph/qsgdefaultlayer_p.h b/src/quick/scenegraph/qsgdefaultlayer_p.h
index ae39994096..7b09293095 100644
--- a/src/quick/scenegraph/qsgdefaultlayer_p.h
+++ b/src/quick/scenegraph/qsgdefaultlayer_p.h
@@ -113,6 +113,9 @@ public:
QRectF normalizedTextureSubRect() const Q_DECL_OVERRIDE;
+ int samples() const { return m_samples; }
+ void setSamples(int samples) Q_DECL_OVERRIDE { m_samples = samples; }
+
public Q_SLOTS:
void markDirtyTexture() Q_DECL_OVERRIDE;
void invalidated() Q_DECL_OVERRIDE;
@@ -138,6 +141,7 @@ private:
#endif
QSGDefaultRenderContext *m_context;
+ int m_samples;
uint m_mipmap : 1;
uint m_live : 1;
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
index b343f89cc0..95f3555994 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp
+++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
@@ -45,7 +45,6 @@
#include <QtQuick/private/qsgrenderer_p.h>
#include <QtQuick/private/qsgatlastexture_p.h>
#include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h>
-#include <QtQuick/private/qsgdistancefieldutil_p.h>
QT_BEGIN_NAMESPACE
@@ -159,8 +158,8 @@ void QSGDefaultRenderContext::invalidate()
delete m_depthStencilManager;
m_depthStencilManager = 0;
- delete m_distanceFieldCacheManager;
- m_distanceFieldCacheManager = 0;
+ qDeleteAll(m_glyphCaches);
+ m_glyphCaches.clear();
if (m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this))
m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant());
@@ -260,7 +259,7 @@ void QSGDefaultRenderContext::compileShader(QSGMaterialShader *shader, QSGMateri
if (vertexCode || fragmentCode) {
Q_ASSERT_X((material->flags() & QSGMaterial::CustomCompileStep) == 0,
"QSGRenderContext::compile()",
- "materials with custom compile step cannot have custom vertex/fragment code");
+ "materials with custom compile step cannot have modified vertex or fragment code");
QOpenGLShaderProgram *p = shader->program();
p->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexCode ? vertexCode : shader->vertexShader());
p->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentCode ? fragmentCode : shader->fragmentShader());
@@ -272,6 +271,26 @@ void QSGDefaultRenderContext::compileShader(QSGMaterialShader *shader, QSGMateri
}
}
+QString QSGDefaultRenderContext::fontKey(const QRawFont &font)
+{
+ QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine;
+ if (!fe->faceId().filename.isEmpty()) {
+ QByteArray keyName = fe->faceId().filename;
+ if (font.style() != QFont::StyleNormal)
+ keyName += QByteArray(" I");
+ if (font.weight() != QFont::Normal)
+ keyName += ' ' + QByteArray::number(font.weight());
+ keyName += QByteArray(" DF");
+ return QString::fromUtf8(keyName);
+ } else {
+ return QString::fromLatin1("%1_%2_%3_%4")
+ .arg(font.familyName())
+ .arg(font.styleName())
+ .arg(font.weight())
+ .arg(font.style());
+ }
+}
+
void QSGDefaultRenderContext::initializeShader(QSGMaterialShader *shader)
{
shader->program()->bind();
@@ -291,13 +310,11 @@ QSGDefaultRenderContext *QSGDefaultRenderContext::from(QOpenGLContext *context)
QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font)
{
- if (!m_distanceFieldCacheManager)
- m_distanceFieldCacheManager = new QSGDistanceFieldGlyphCacheManager;
-
- QSGDistanceFieldGlyphCache *cache = m_distanceFieldCacheManager->cache(font);
+ QString key = fontKey(font);
+ QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(key, 0);
if (!cache) {
- cache = new QSGDefaultDistanceFieldGlyphCache(m_distanceFieldCacheManager, openglContext(), font);
- m_distanceFieldCacheManager->insertCache(font, cache);
+ cache = new QSGDefaultDistanceFieldGlyphCache(openglContext(), font);
+ m_glyphCaches.insert(key, cache);
}
return cache;
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
index 0aed46b658..2537a06988 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext_p.h
+++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
@@ -96,6 +96,8 @@ public:
int maxTextureSize() const override { return m_maxTextureSize; }
protected:
+ static QString fontKey(const QRawFont &font);
+
QOpenGLContext *m_gl;
QSGDepthStencilBufferManager *m_depthStencilManager;
int m_maxTextureSize;
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
index 456a197ba1..32eda2d142 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
@@ -39,7 +39,6 @@
#include "qsgdistancefieldglyphnode_p.h"
#include "qsgdistancefieldglyphnode_p_p.h"
-#include <QtQuick/private/qsgdistancefieldutil_p.h>
#include <QtQuick/private/qsgcontext_p.h>
QT_BEGIN_NAMESPACE
@@ -76,9 +75,6 @@ QSGDistanceFieldGlyphNode::~QSGDistanceFieldGlyphNode()
m_glyph_cache->unregisterGlyphNode(this);
m_glyph_cache->unregisterOwnerElement(ownerElement());
}
-
- while (m_nodesToDelete.count())
- delete m_nodesToDelete.takeLast();
}
void QSGDistanceFieldGlyphNode::setColor(const QColor &color)
@@ -158,9 +154,6 @@ void QSGDistanceFieldGlyphNode::preprocess()
{
Q_ASSERT(m_glyph_cache);
- while (m_nodesToDelete.count())
- delete m_nodesToDelete.takeLast();
-
m_glyph_cache->processPendingGlyphs();
m_glyph_cache->update();
@@ -188,13 +181,12 @@ void QSGDistanceFieldGlyphNode::updateGeometry()
// Remove previously created sub glyph nodes
// We assume all the children are sub glyph nodes
QSGNode *subnode = firstChild();
+ QSGNode *nextNode = 0;
while (subnode) {
- // We can't delete the node now as it might be in the preprocess list
- // It will be deleted in the next preprocess
- m_nodesToDelete.append(subnode);
- subnode = subnode->nextSibling();
+ nextNode = subnode->nextSibling();
+ delete subnode;
+ subnode = nextNode;
}
- removeAllChildNodes();
QSGGeometry *g = geometry();
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp
index ca91e5d85f..a67c659c99 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qsgdistancefieldglyphnode_p_p.h"
-#include <QtQuick/private/qsgdistancefieldutil_p.h>
#include <QtQuick/private/qsgtexture_p.h>
#include <QtGui/qopenglfunctions.h>
#include <QtGui/qsurface.h>
@@ -52,13 +51,13 @@ class QSGDistanceFieldTextMaterialShader : public QSGMaterialShader
public:
QSGDistanceFieldTextMaterialShader();
- virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
- virtual char const *const *attributeNames() const;
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
+ char const *const *attributeNames() const override;
protected:
- virtual void initialize();
+ void initialize() override;
- void updateAlphaRange(ThresholdFunc thresholdFunc, AntialiasingSpreadFunc spreadFunc);
+ void updateAlphaRange();
void updateColor(const QVector4D &c);
void updateTextureScale(const QVector2D &ts);
@@ -98,7 +97,31 @@ QSGDistanceFieldTextMaterialShader::QSGDistanceFieldTextMaterialShader()
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldtext.frag"));
}
-void QSGDistanceFieldTextMaterialShader::updateAlphaRange(ThresholdFunc thresholdFunc, AntialiasingSpreadFunc spreadFunc)
+static float qt_sg_envFloat(const char *name, float defaultValue)
+{
+ if (Q_LIKELY(!qEnvironmentVariableIsSet(name)))
+ return defaultValue;
+ bool ok = false;
+ const float value = qgetenv(name).toFloat(&ok);
+ return ok ? value : defaultValue;
+}
+
+static float thresholdFunc(float glyphScale)
+{
+ static const float base = qt_sg_envFloat("QT_DF_BASE", 0.5f);
+ static const float baseDev = qt_sg_envFloat("QT_DF_BASEDEVIATION", 0.065f);
+ static const float devScaleMin = qt_sg_envFloat("QT_DF_SCALEFORMAXDEV", 0.15f);
+ static const float devScaleMax = qt_sg_envFloat("QT_DF_SCALEFORNODEV", 0.3f);
+ return base - ((qBound(devScaleMin, glyphScale, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin) * -baseDev + baseDev);
+}
+
+static float spreadFunc(float glyphScale)
+{
+ static const float range = qt_sg_envFloat("QT_DF_RANGE", 0.06f);
+ return range / glyphScale;
+}
+
+void QSGDistanceFieldTextMaterialShader::updateAlphaRange()
{
float combinedScale = m_fontScale * m_matrixScale;
float base = thresholdFunc(combinedScale);
@@ -169,8 +192,7 @@ void QSGDistanceFieldTextMaterialShader::updateState(const RenderState &state, Q
updateRange = true;
}
if (updateRange) {
- updateAlphaRange(material->glyphCache()->manager()->thresholdFunc(),
- material->glyphCache()->manager()->antialiasingSpreadFunc());
+ updateAlphaRange();
}
Q_ASSERT(material->glyphCache());
@@ -261,10 +283,10 @@ class DistanceFieldStyledTextMaterialShader : public QSGDistanceFieldTextMateria
public:
DistanceFieldStyledTextMaterialShader();
- virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
protected:
- virtual void initialize();
+ void initialize() override;
int m_styleColor_id;
};
@@ -329,12 +351,12 @@ class DistanceFieldOutlineTextMaterialShader : public DistanceFieldStyledTextMat
public:
DistanceFieldOutlineTextMaterialShader();
- virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
protected:
- virtual void initialize();
+ void initialize() override;
- void updateOutlineAlphaRange(ThresholdFunc thresholdFunc, AntialiasingSpreadFunc spreadFunc, int dfRadius);
+ void updateOutlineAlphaRange(int dfRadius);
int m_outlineAlphaMax0_id;
int m_outlineAlphaMax1_id;
@@ -355,9 +377,7 @@ void DistanceFieldOutlineTextMaterialShader::initialize()
m_outlineAlphaMax1_id = program()->uniformLocation("outlineAlphaMax1");
}
-void DistanceFieldOutlineTextMaterialShader::updateOutlineAlphaRange(ThresholdFunc thresholdFunc,
- AntialiasingSpreadFunc spreadFunc,
- int dfRadius)
+void DistanceFieldOutlineTextMaterialShader::updateOutlineAlphaRange(int dfRadius)
{
float combinedScale = m_fontScale * m_matrixScale;
float base = thresholdFunc(combinedScale);
@@ -381,9 +401,7 @@ void DistanceFieldOutlineTextMaterialShader::updateState(const RenderState &stat
if (oldMaterial == 0
|| material->fontScale() != oldMaterial->fontScale()
|| state.isMatrixDirty())
- updateOutlineAlphaRange(material->glyphCache()->manager()->thresholdFunc(),
- material->glyphCache()->manager()->antialiasingSpreadFunc(),
- material->glyphCache()->distanceFieldRadius());
+ updateOutlineAlphaRange(material->glyphCache()->distanceFieldRadius());
}
@@ -413,10 +431,10 @@ class DistanceFieldShiftedStyleTextMaterialShader : public DistanceFieldStyledTe
public:
DistanceFieldShiftedStyleTextMaterialShader();
- virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
protected:
- virtual void initialize();
+ void initialize() override;
void updateShift(qreal fontScale, const QPointF& shift);
@@ -492,10 +510,10 @@ class QSGHiQSubPixelDistanceFieldTextMaterialShader : public QSGDistanceFieldTex
public:
QSGHiQSubPixelDistanceFieldTextMaterialShader();
- virtual void initialize();
- virtual void activate();
- virtual void deactivate();
- virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
+ void initialize() override;
+ void activate() override;
+ void deactivate() override;
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
private:
int m_fontScale_id;
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
index c0c6bda718..7008f20925 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
@@ -59,7 +59,6 @@
QT_BEGIN_NAMESPACE
class QSGRenderContext;
-class QSGDistanceFieldGlyphCacheManager;
class QSGDistanceFieldTextMaterial;
class QSGDistanceFieldGlyphNode: public QSGGlyphNode, public QSGDistanceFieldGlyphConsumer
{
@@ -107,7 +106,6 @@ private:
AntialiasingMode m_antialiasingMode;
QRectF m_boundingRect;
const QSGDistanceFieldGlyphCache::Texture *m_texture;
- QLinkedList<QSGNode *> m_nodesToDelete;
struct GlyphInfo {
QVector<quint32> indexes;
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index bc65dc1bc3..4a69b770ed 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -139,25 +139,25 @@ public:
QSGGuiThreadRenderLoop();
~QSGGuiThreadRenderLoop();
- void show(QQuickWindow *window);
- void hide(QQuickWindow *window);
+ void show(QQuickWindow *window) override;
+ void hide(QQuickWindow *window) override;
- void windowDestroyed(QQuickWindow *window);
+ void windowDestroyed(QQuickWindow *window) override;
void renderWindow(QQuickWindow *window);
- void exposureChanged(QQuickWindow *window);
- QImage grab(QQuickWindow *window);
+ void exposureChanged(QQuickWindow *window) override;
+ QImage grab(QQuickWindow *window) override;
- void maybeUpdate(QQuickWindow *window);
- void update(QQuickWindow *window) { maybeUpdate(window); } // identical for this implementation.
- void handleUpdateRequest(QQuickWindow *);
+ void maybeUpdate(QQuickWindow *window) override;
+ void update(QQuickWindow *window) override { maybeUpdate(window); } // identical for this implementation.
+ void handleUpdateRequest(QQuickWindow *) override;
- void releaseResources(QQuickWindow *) { }
+ void releaseResources(QQuickWindow *) override { }
- QAnimationDriver *animationDriver() const { return 0; }
+ QAnimationDriver *animationDriver() const override { return 0; }
- QSGContext *sceneGraphContext() const;
- QSGRenderContext *createRenderContext(QSGContext *) const { return rc; }
+ QSGContext *sceneGraphContext() const override;
+ QSGRenderContext *createRenderContext(QSGContext *) const override { return rc; }
struct WindowData {
bool updatePending : 1;
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index 3a8e673c0d..1e5e61e43b 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -295,8 +295,8 @@ public:
void invalidateOpenGL(QQuickWindow *window, bool inDestructor, QOffscreenSurface *backupSurface);
void initializeOpenGL();
- bool event(QEvent *);
- void run();
+ bool event(QEvent *) override;
+ void run() override;
void syncAndRender();
void sync(bool inExpose);
diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri
index 38c3b8dd85..b5c72f521c 100644
--- a/src/quick/scenegraph/scenegraph.pri
+++ b/src/quick/scenegraph/scenegraph.pri
@@ -45,7 +45,6 @@ HEADERS += \
$$PWD/util/qsgtexture.h \
$$PWD/util/qsgtexture_p.h \
$$PWD/util/qsgtextureprovider.h \
- $$PWD/util/qsgdistancefieldutil_p.h \
$$PWD/util/qsgflatcolormaterial.h \
$$PWD/util/qsgsimplematerial.h \
$$PWD/util/qsgtexturematerial.h \
@@ -62,7 +61,6 @@ SOURCES += \
$$PWD/util/qsgsimpletexturenode.cpp \
$$PWD/util/qsgtexture.cpp \
$$PWD/util/qsgtextureprovider.cpp \
- $$PWD/util/qsgdistancefieldutil.cpp \
$$PWD/util/qsgflatcolormaterial.cpp \
$$PWD/util/qsgsimplematerial.cpp \
$$PWD/util/qsgtexturematerial.cpp \
@@ -222,3 +220,18 @@ qtConfig(opengl(es1|es2)?) {
$$PWD/shaders/visualization.frag \
$$PWD/shaders/visualization.vert
}
+
+# Compressed Texture API
+HEADERS += \
+ $$PWD/util/qsgtexturereader_p.h
+
+SOURCES += \
+ $$PWD/util/qsgtexturereader.cpp
+
+qtConfig(opengl(es1|es2)?) {
+ HEADERS += \
+ $$PWD/compressedtexture/qsgpkmhandler_p.h
+
+ SOURCES += \
+ $$PWD/compressedtexture/qsgpkmhandler.cpp
+}
diff --git a/src/quick/scenegraph/util/qsgdistancefieldutil.cpp b/src/quick/scenegraph/util/qsgdistancefieldutil.cpp
deleted file mode 100644
index 84df7f3393..0000000000
--- a/src/quick/scenegraph/util/qsgdistancefieldutil.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "qsgdistancefieldutil_p.h"
-
-#include <private/qsgadaptationlayer_p.h>
-#if QT_CONFIG(opengl)
-# include <QtGui/private/qopenglengineshadersource_p.h>
-#endif
-#include <QtQuick/private/qsgcontext_p.h>
-
-QT_BEGIN_NAMESPACE
-
-static float qt_sg_envFloat(const char *name, float defaultValue)
-{
- if (Q_LIKELY(!qEnvironmentVariableIsSet(name)))
- return defaultValue;
- bool ok = false;
- const float value = qgetenv(name).toFloat(&ok);
- return ok ? value : defaultValue;
-}
-
-static float defaultThresholdFunc(float glyphScale)
-{
- static const float base = qt_sg_envFloat("QT_DF_BASE", 0.5f);
- static const float baseDev = qt_sg_envFloat("QT_DF_BASEDEVIATION", 0.065f);
- static const float devScaleMin = qt_sg_envFloat("QT_DF_SCALEFORMAXDEV", 0.15f);
- static const float devScaleMax = qt_sg_envFloat("QT_DF_SCALEFORNODEV", 0.3f);
- return base - ((qBound(devScaleMin, glyphScale, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin) * -baseDev + baseDev);
-}
-
-static float defaultAntialiasingSpreadFunc(float glyphScale)
-{
- static const float range = qt_sg_envFloat("QT_DF_RANGE", 0.06f);
- return range / glyphScale;
-}
-
-QSGDistanceFieldGlyphCacheManager::QSGDistanceFieldGlyphCacheManager()
- : m_threshold_func(defaultThresholdFunc)
- , m_antialiasingSpread_func(defaultAntialiasingSpreadFunc)
-{
-}
-
-QSGDistanceFieldGlyphCacheManager::~QSGDistanceFieldGlyphCacheManager()
-{
- qDeleteAll(m_caches);
-}
-
-QSGDistanceFieldGlyphCache *QSGDistanceFieldGlyphCacheManager::cache(const QRawFont &font)
-{
- return m_caches.value(fontKey(font), 0);
-}
-
-void QSGDistanceFieldGlyphCacheManager::insertCache(const QRawFont &font, QSGDistanceFieldGlyphCache *cache)
-{
- m_caches.insert(fontKey(font), cache);
-}
-
-QString QSGDistanceFieldGlyphCacheManager::fontKey(const QRawFont &font)
-{
- QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine;
- if (!fe->faceId().filename.isEmpty()) {
- QByteArray keyName = fe->faceId().filename;
- if (font.style() != QFont::StyleNormal)
- keyName += QByteArray(" I");
- if (font.weight() != QFont::Normal)
- keyName += ' ' + QByteArray::number(font.weight());
- keyName += QByteArray(" DF");
- return QString::fromUtf8(keyName);
- } else {
- return QString::fromLatin1("%1_%2_%3_%4")
- .arg(font.familyName())
- .arg(font.styleName())
- .arg(font.weight())
- .arg(font.style());
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp
index 8ab7669891..a0c71b5340 100644
--- a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp
+++ b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp
@@ -50,13 +50,13 @@ class FlatColorMaterialShader : public QSGMaterialShader
public:
FlatColorMaterialShader();
- virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
- virtual char const *const *attributeNames() const;
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
+ char const *const *attributeNames() const override;
static QSGMaterialType type;
private:
- virtual void initialize();
+ void initialize() override;
#if QT_CONFIG(opengl)
int m_matrix_id;
int m_color_id;
diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp
index 4abee3d568..4f11d95e70 100644
--- a/src/quick/scenegraph/util/qsgtexture.cpp
+++ b/src/quick/scenegraph/util/qsgtexture.cpp
@@ -256,11 +256,15 @@ static void qt_debug_remove_texture(QSGTexture* texture)
Specifies how the texture should treat texture coordinates.
- \value Repeat Only the factional part of the texture coordiante is
+ \value Repeat Only the fractional part of the texture coordinate is
used, causing values above 1 and below 0 to repeat.
\value ClampToEdge Values above 1 are clamped to 1 and values
below 0 are clamped to 0.
+
+ \value MirroredRepeat When the texture coordinate is even, only the
+ fractional part is used. When odd, the texture coordinate is set to
+ \c{1 - fractional part}. This value has been introduced in Qt 5.10.
*/
/*!
@@ -605,7 +609,9 @@ void QSGTexture::updateBindOptions(bool force)
if (force || d->wrapChanged) {
#ifndef QT_NO_DEBUG
- if (d->horizontalWrap == Repeat || d->verticalWrap == Repeat) {
+ if (d->horizontalWrap == Repeat || d->verticalWrap == Repeat
+ || d->horizontalWrap == MirroredRepeat || d->verticalWrap == MirroredRepeat)
+ {
bool npotSupported = QOpenGLFunctions(QOpenGLContext::currentContext()).hasOpenGLFeature(QOpenGLFunctions::NPOTTextures);
QSize size = textureSize();
bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
@@ -613,8 +619,18 @@ void QSGTexture::updateBindOptions(bool force)
qWarning("Scene Graph: This system does not support the REPEAT wrap mode for non-power-of-two textures.");
}
#endif
- funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, d->horizontalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
- funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, d->verticalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
+ GLenum wrapS = GL_CLAMP_TO_EDGE;
+ if (d->horizontalWrap == Repeat)
+ wrapS = GL_REPEAT;
+ else if (d->horizontalWrap == MirroredRepeat)
+ wrapS = GL_MIRRORED_REPEAT;
+ GLenum wrapT = GL_CLAMP_TO_EDGE;
+ if (d->verticalWrap == Repeat)
+ wrapT = GL_REPEAT;
+ else if (d->verticalWrap == MirroredRepeat)
+ wrapT = GL_MIRRORED_REPEAT;
+ funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS);
+ funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT);
d->wrapChanged = false;
}
#else
diff --git a/src/quick/scenegraph/util/qsgtexture.h b/src/quick/scenegraph/util/qsgtexture.h
index 035acc02b1..032129434e 100644
--- a/src/quick/scenegraph/util/qsgtexture.h
+++ b/src/quick/scenegraph/util/qsgtexture.h
@@ -58,7 +58,8 @@ public:
enum WrapMode {
Repeat,
- ClampToEdge
+ ClampToEdge,
+ MirroredRepeat
};
enum Filtering {
diff --git a/src/quick/scenegraph/util/qsgtexture_p.h b/src/quick/scenegraph/util/qsgtexture_p.h
index 36f9b802ba..52dc6db2d0 100644
--- a/src/quick/scenegraph/util/qsgtexture_p.h
+++ b/src/quick/scenegraph/util/qsgtexture_p.h
@@ -71,8 +71,8 @@ public:
uint filteringChanged : 1;
uint anisotropyChanged : 1;
- uint horizontalWrap : 1;
- uint verticalWrap : 1;
+ uint horizontalWrap : 2;
+ uint verticalWrap : 2;
uint mipmapMode : 2;
uint filterMode : 2;
uint anisotropyLevel: 3;
diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp
index c536445e82..fbc8f27a63 100644
--- a/src/quick/scenegraph/util/qsgtexturematerial.cpp
+++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp
@@ -280,7 +280,7 @@ void QSGOpaqueTextureMaterial::setTexture(QSGTexture *texture)
Returns this material's horizontal wrap mode.
- The default horizontal wrap mode is \c QSGTexutre::ClampToEdge.
+ The default horizontal wrap mode is \c QSGTexture::ClampToEdge.
*/
@@ -301,7 +301,7 @@ void QSGOpaqueTextureMaterial::setTexture(QSGTexture *texture)
Returns this material's vertical wrap mode.
- The default vertical wrap mode is \c QSGTexutre::ClampToEdge.
+ The default vertical wrap mode is \c QSGTexture::ClampToEdge.
*/
diff --git a/src/quick/scenegraph/util/qsgtexturereader.cpp b/src/quick/scenegraph/util/qsgtexturereader.cpp
new file mode 100644
index 0000000000..61729ada18
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgtexturereader.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** 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 "qsgtexturereader_p.h"
+
+#include <private/qtquickglobal_p.h>
+
+#if QT_CONFIG(opengl)
+#include <private/qsgpkmhandler_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QSGTextureReader::QSGTextureReader()
+{
+
+}
+
+QQuickTextureFactory *QSGTextureReader::read(QIODevice *device, const QByteArray &format)
+{
+#if QT_CONFIG(opengl)
+ if (format == QByteArrayLiteral("pkm")) {
+ QSGPkmHandler handler;
+ return handler.read(device);
+ }
+#else
+ Q_UNUSED(device)
+ Q_UNUSED(format)
+#endif
+ return nullptr;
+}
+
+bool QSGTextureReader::isTexture(QIODevice *device, const QByteArray &format)
+{
+#if QT_CONFIG(opengl)
+ if (format == QByteArrayLiteral("pkm")) {
+ return device->peek(4) == QByteArrayLiteral("PKM ");
+ }
+#else
+ Q_UNUSED(device)
+ Q_UNUSED(format)
+#endif
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgtexturereader_p.h b/src/quick/scenegraph/util/qsgtexturereader_p.h
new file mode 100644
index 0000000000..7d2fc314a6
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgtexturereader_p.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** 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 QSGTEXTUREREADER_H
+#define QSGTEXTUREREADER_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 <QString>
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+class QQuickTextureFactory;
+
+class QSGTextureReader
+{
+public:
+ QSGTextureReader();
+
+ static QQuickTextureFactory *read(QIODevice *device, const QByteArray &format);
+ static bool isTexture(QIODevice *device, const QByteArray &format);
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGTEXTUREREADER_H
diff --git a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp
index 8c305d7fd4..42c589b14a 100644
--- a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp
+++ b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp
@@ -48,13 +48,13 @@ class QSGVertexColorMaterialShader : public QSGMaterialShader
public:
QSGVertexColorMaterialShader();
- virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
- virtual char const *const *attributeNames() const;
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
+ char const *const *attributeNames() const override;
static QSGMaterialType type;
private:
- virtual void initialize();
+ void initialize() override;
#if QT_CONFIG(opengl)
int m_matrix_id;
int m_opacity_id;
diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp
index 3ac02cd259..a7950e2b33 100644
--- a/src/quick/util/qquickanimatorjob.cpp
+++ b/src/quick/util/qquickanimatorjob.cpp
@@ -321,8 +321,10 @@ void QQuickTransformAnimatorJob::preSync()
m_helper = nullptr;
}
- if (!m_target)
+ if (!m_target) {
+ invalidate();
return;
+ }
if (!m_helper) {
m_helper = qquick_transform_animatorjob_helper_store()->acquire(m_target);
@@ -342,27 +344,6 @@ void QQuickTransformAnimatorJob::preSync()
m_helper->sync();
}
-void QQuickTransformAnimatorJob::postSync()
-{
- Q_ASSERT((m_helper != nullptr) == (m_target != nullptr)); // If there is a target, there should also be a helper, ref: preSync
- Q_ASSERT(!m_helper || m_helper->item == m_target); // If there is a helper, it should point to our target
-
- if (!m_target || !m_helper) {
- invalidate();
- return;
- }
-
- QQuickItemPrivate *d = QQuickItemPrivate::get(m_target);
-#if QT_CONFIG(quick_shadereffect)
- if (d->extra.isAllocated()
- && d->extra->layer
- && d->extra->layer->enabled()) {
- d = QQuickItemPrivate::get(d->extra->layer->m_effectSource);
- }
-#endif
- m_helper->node = d->itemNode();
-}
-
void QQuickTransformAnimatorJob::invalidate()
{
if (m_helper)
diff --git a/src/quick/util/qquickanimatorjob_p.h b/src/quick/util/qquickanimatorjob_p.h
index a3ced4c21b..777da2ee6c 100644
--- a/src/quick/util/qquickanimatorjob_p.h
+++ b/src/quick/util/qquickanimatorjob_p.h
@@ -235,7 +235,6 @@ public:
protected:
QQuickTransformAnimatorJob();
- void postSync() override;
void invalidate() override;
Helper *m_helper;
diff --git a/src/quick/util/qquickglobal.cpp b/src/quick/util/qquickglobal.cpp
index 5ba849c57f..6df23cdff5 100644
--- a/src/quick/util/qquickglobal.cpp
+++ b/src/quick/util/qquickglobal.cpp
@@ -61,7 +61,7 @@ QT_BEGIN_NAMESPACE
class QQuickColorProvider : public QQmlColorProvider
{
public:
- QVariant colorFromString(const QString &s, bool *ok)
+ QVariant colorFromString(const QString &s, bool *ok) override
{
QColor c(s);
if (c.isValid()) {
@@ -73,7 +73,7 @@ public:
return QVariant();
}
- unsigned rgbaFromString(const QString &s, bool *ok)
+ unsigned rgbaFromString(const QString &s, bool *ok) override
{
QColor c(s);
if (c.isValid()) {
@@ -95,36 +95,36 @@ public:
return QString();
}
- QVariant fromRgbF(double r, double g, double b, double a)
+ QVariant fromRgbF(double r, double g, double b, double a) override
{
return QVariant(QColor::fromRgbF(r, g, b, a));
}
- QVariant fromHslF(double h, double s, double l, double a)
+ QVariant fromHslF(double h, double s, double l, double a) override
{
return QVariant(QColor::fromHslF(h, s, l, a));
}
- QVariant fromHsvF(double h, double s, double v, double a)
+ QVariant fromHsvF(double h, double s, double v, double a) override
{
return QVariant(QColor::fromHsvF(h, s, v, a));
}
- QVariant lighter(const QVariant &var, qreal factor)
+ QVariant lighter(const QVariant &var, qreal factor) override
{
QColor color = var.value<QColor>();
color = color.lighter(int(qRound(factor*100.)));
return QVariant::fromValue(color);
}
- QVariant darker(const QVariant &var, qreal factor)
+ QVariant darker(const QVariant &var, qreal factor) override
{
QColor color = var.value<QColor>();
color = color.darker(int(qRound(factor*100.)));
return QVariant::fromValue(color);
}
- QVariant tint(const QVariant &baseVar, const QVariant &tintVar)
+ QVariant tint(const QVariant &baseVar, const QVariant &tintVar) override
{
QColor tintColor = tintVar.value<QColor>();
@@ -302,6 +302,7 @@ public:
QV4::ScopedValue vweight(scope, obj->get((s = v4->newString(QStringLiteral("weight")))));
QV4::ScopedValue vwspac(scope, obj->get((s = v4->newString(QStringLiteral("wordSpacing")))));
QV4::ScopedValue vhint(scope, obj->get((s = v4->newString(QStringLiteral("hintingPreference")))));
+ QV4::ScopedValue vkerning(scope, obj->get((s = v4->newString(QStringLiteral("kerning")))));
// pull out the values, set ok to true if at least one valid field is given.
if (vbold->isBoolean()) {
@@ -356,6 +357,10 @@ public:
retn.setHintingPreference(static_cast<QFont::HintingPreference>(vhint->integerValue()));
if (ok) *ok = true;
}
+ if (vkerning->isBoolean()) {
+ retn.setKerning(vkerning->booleanValue());
+ if (ok) *ok = true;
+ }
return retn;
}
@@ -778,13 +783,13 @@ public:
class QQuickGuiProvider : public QQmlGuiProvider
{
public:
- QQuickApplication *application(QObject *parent)
+ QQuickApplication *application(QObject *parent) override
{
return new QQuickApplication(parent);
}
#if QT_CONFIG(im)
- QInputMethod *inputMethod()
+ QInputMethod *inputMethod() override
{
QInputMethod *im = qGuiApp->inputMethod();
QQmlEngine::setObjectOwnership(im, QQmlEngine::CppOwnership);
@@ -792,20 +797,20 @@ public:
}
#endif
- QStyleHints *styleHints()
+ QStyleHints *styleHints() override
{
QStyleHints *sh = qGuiApp->styleHints();
QQmlEngine::setObjectOwnership(sh, QQmlEngine::CppOwnership);
return sh;
}
- QStringList fontFamilies()
+ QStringList fontFamilies() override
{
QFontDatabase database;
return database.families();
}
- bool openUrlExternally(QUrl &url)
+ bool openUrlExternally(QUrl &url) override
{
#ifndef QT_NO_DESKTOPSERVICES
return QDesktopServices::openUrl(url);
@@ -814,6 +819,11 @@ public:
return false;
#endif
}
+
+ QString pluginName() const override
+ {
+ return QGuiApplication::platformName();
+ }
};
diff --git a/src/quick/util/qquickimageprovider.cpp b/src/quick/util/qquickimageprovider.cpp
index c80025f968..b4a6b9e1f6 100644
--- a/src/quick/util/qquickimageprovider.cpp
+++ b/src/quick/util/qquickimageprovider.cpp
@@ -183,7 +183,12 @@ QString QQuickImageResponse::errorString() const
It may be reimplemented to cancel a request in the provider side, however, it is not mandatory.
- A cancelled QQuickImageResponse still needs to emit finished().
+ A cancelled QQuickImageResponse still needs to emit finished() so that the
+ engine may clean up the QQuickImageResponse.
+
+ \note finished() should not be emitted until the response is complete,
+ regardless of whether or not cancel() was called. If it is called prematurely,
+ the engine may destroy the response while it is still active, leading to a crash.
*/
void QQuickImageResponse::cancel()
{
@@ -192,7 +197,12 @@ void QQuickImageResponse::cancel()
/*!
\fn void QQuickImageResponse::finished()
- Signals that the job execution has finished (be it successfully, because an error happened or because it was cancelled).
+ Signals that the job execution has finished (be it successfully, because an
+ error happened or because it was cancelled).
+
+ \note Emission of this signal must be the final action the response performs:
+ once the signal is received, the response will subsequently be destroyed by
+ the engine.
*/
/*!
diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp
index 8abb9377a6..15defdc01b 100644
--- a/src/quick/util/qquickpath.cpp
+++ b/src/quick/util/qquickpath.cpp
@@ -68,7 +68,7 @@ QT_BEGIN_NAMESPACE
\instantiates QQuickPath
\inqmlmodule QtQuick
\ingroup qtquick-animation-paths
- \brief Defines a path for use by \l PathView
+ \brief Defines a path for use by \l PathView and \l Shape
A Path is composed of one or more path segments - PathLine, PathQuad,
PathCubic, PathArc, PathCurve, PathSvg.
@@ -79,13 +79,85 @@ QT_BEGIN_NAMESPACE
PathAttribute allows named attributes with values to be defined
along the path.
- \sa PathView, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
+ Path and the other types for specifying path elements are shared between
+ \l PathView and \l Shape. The following table provides an overview of the
+ applicability of the various path elements:
+
+ \table
+ \header
+ \li Element
+ \li PathView
+ \li Shape
+ \li Shape, GL_NV_path_rendering
+ \li Shape, software
+ \row
+ \li PathMove
+ \li N/A
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
+ \li PathLine
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
+ \li PathQuad
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
+ \li PathCubic
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
+ \li PathArc
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
+ \li PathSvg
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
+ \li PathAttribute
+ \li Yes
+ \li N/A
+ \li N/A
+ \li N/A
+ \row
+ \li PathPercent
+ \li Yes
+ \li N/A
+ \li N/A
+ \li N/A
+ \row
+ \li PathCurve
+ \li Yes
+ \li No
+ \li No
+ \li No
+ \endtable
+
+ \sa PathView, Shape, PathAttribute, PathPercent, PathLine, PathMove, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
*/
QQuickPath::QQuickPath(QObject *parent)
: QObject(*(new QQuickPathPrivate), parent)
{
}
+QQuickPath::QQuickPath(QQuickPathPrivate &dd, QObject *parent)
+ : QObject(dd, parent)
+{
+}
+
QQuickPath::~QQuickPath()
{
}
@@ -1003,7 +1075,7 @@ void QQuickPathAttribute::setValue(qreal value)
}
\endqml
- \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
+ \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg, PathMove
*/
/*!
@@ -1046,6 +1118,64 @@ void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data)
/****************************************************************************/
/*!
+ \qmltype PathMove
+ \instantiates QQuickPathMove
+ \inqmlmodule QtQuick
+ \ingroup qtquick-animation-paths
+ \brief Moves the Path's position
+
+ The example below creates a path consisting of two horizontal lines with
+ some empty space between them. All three segments have a width of 100:
+
+ \qml
+ Path {
+ startX: 0; startY: 100
+ PathLine { relativeX: 100; y: 100 }
+ PathMove { relativeX: 100; y: 100 }
+ PathLine { relativeX: 100; y: 100 }
+ }
+ \endqml
+
+ \note PathMove should not be used in a Path associated with a PathView. Use
+ PathLine instead. For ShapePath however it is important to distinguish
+ between the operations of drawing a straight line and moving the path
+ position without drawing anything.
+
+ \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg, PathLine
+*/
+
+/*!
+ \qmlproperty real QtQuick::PathMove::x
+ \qmlproperty real QtQuick::PathMove::y
+
+ Defines the position to move to.
+
+ \sa relativeX, relativeY
+*/
+
+/*!
+ \qmlproperty real QtQuick::PathMove::relativeX
+ \qmlproperty real QtQuick::PathMove::relativeY
+
+ Defines the position to move to relative to its start.
+
+ If both a relative and absolute end position are specified for a single axis, the relative
+ position will be used.
+
+ Relative and absolute positions can be mixed, for example it is valid to set a relative x
+ and an absolute y.
+
+ \sa x, y
+*/
+
+void QQuickPathMove::addToPath(QPainterPath &path, const QQuickPathData &data)
+{
+ path.moveTo(positionForCurve(data, path.currentPosition()));
+}
+
+/****************************************************************************/
+
+/*!
\qmltype PathQuad
\instantiates QQuickPathQuad
\inqmlmodule QtQuick
@@ -1641,6 +1771,7 @@ void QQuickPathArc::setRadiusX(qreal radius)
_radiusX = radius;
emit radiusXChanged();
+ emit changed();
}
qreal QQuickPathArc::radiusY() const
@@ -1655,6 +1786,7 @@ void QQuickPathArc::setRadiusY(qreal radius)
_radiusY = radius;
emit radiusYChanged();
+ emit changed();
}
/*!
@@ -1688,6 +1820,7 @@ void QQuickPathArc::setUseLargeArc(bool largeArc)
_useLargeArc = largeArc;
emit useLargeArcChanged();
+ emit changed();
}
/*!
@@ -1719,6 +1852,43 @@ void QQuickPathArc::setDirection(ArcDirection direction)
_direction = direction;
emit directionChanged();
+ emit changed();
+}
+
+/*!
+ \qmlproperty real QtQuick::PathArc::xAxisRotation
+
+ Defines the rotation of the arc, in degrees. The default value is 0.
+
+ An arc is a section of circles or ellipses. Given the radius and the start
+ and end points, there are two ellipses that connect the points. This
+ property defines the rotation of the X axis of these ellipses.
+
+ \note The value is only useful when the x and y radius differ, meaning the
+ arc is a section of ellipses.
+
+ The following QML demonstrates how different radius values can be used to change
+ the shape of the arc:
+ \table
+ \row
+ \li \image declarative-arcrotation.png
+ \li \snippet qml/path/arcrotation.qml 0
+ \endtable
+*/
+
+qreal QQuickPathArc::xAxisRotation() const
+{
+ return _xAxisRotation;
+}
+
+void QQuickPathArc::setXAxisRotation(qreal rotation)
+{
+ if (_xAxisRotation == rotation)
+ return;
+
+ _xAxisRotation = rotation;
+ emit xAxisRotationChanged();
+ emit changed();
}
void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
@@ -1728,7 +1898,7 @@ void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
QQuickSvgParser::pathArc(path,
_radiusX,
_radiusY,
- 0, //xAxisRotation
+ _xAxisRotation,
_useLargeArc,
_direction == Clockwise ? 1 : 0,
endPoint.x(),
@@ -1758,6 +1928,11 @@ void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
\endqml
\endtable
+ \note Mixing PathSvg with other type of elements is not always supported.
+ For example, when \l Shape is backed by \c{GL_NV_path_rendering}, a
+ ShapePath can contain one or more PathSvg elements, or one or more other
+ type of elements, but not both.
+
\sa Path, PathLine, PathQuad, PathCubic, PathArc, PathCurve
*/
@@ -1782,6 +1957,7 @@ void QQuickPathSvg::setPath(const QString &path)
_path = path;
emit pathChanged();
+ emit changed();
}
void QQuickPathSvg::addToPath(QPainterPath &path, const QQuickPathData &)
diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h
index c0a96cad4f..b7fde5c272 100644
--- a/src/quick/util/qquickpath_p.h
+++ b/src/quick/util/qquickpath_p.h
@@ -159,6 +159,15 @@ public:
void addToPath(QPainterPath &path, const QQuickPathData &) override;
};
+class Q_QUICK_PRIVATE_EXPORT QQuickPathMove : public QQuickCurve
+{
+ Q_OBJECT
+public:
+ QQuickPathMove(QObject *parent=0) : QQuickCurve(parent) {}
+
+ void addToPath(QPainterPath &path, const QQuickPathData &) override;
+};
+
class Q_QUICK_PRIVATE_EXPORT QQuickPathQuad : public QQuickCurve
{
Q_OBJECT
@@ -281,10 +290,11 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPathArc : public QQuickCurve
Q_PROPERTY(qreal radiusY READ radiusY WRITE setRadiusY NOTIFY radiusYChanged)
Q_PROPERTY(bool useLargeArc READ useLargeArc WRITE setUseLargeArc NOTIFY useLargeArcChanged)
Q_PROPERTY(ArcDirection direction READ direction WRITE setDirection NOTIFY directionChanged)
+ Q_PROPERTY(qreal xAxisRotation READ xAxisRotation WRITE setXAxisRotation NOTIFY xAxisRotationChanged REVISION 2)
public:
QQuickPathArc(QObject *parent=0)
- : QQuickCurve(parent), _radiusX(0), _radiusY(0), _useLargeArc(false), _direction(Clockwise) {}
+ : QQuickCurve(parent), _radiusX(0), _radiusY(0), _useLargeArc(false), _direction(Clockwise), _xAxisRotation(0) {}
enum ArcDirection { Clockwise, Counterclockwise };
Q_ENUM(ArcDirection)
@@ -301,6 +311,9 @@ public:
ArcDirection direction() const;
void setDirection(ArcDirection direction);
+ qreal xAxisRotation() const;
+ void setXAxisRotation(qreal rotation);
+
void addToPath(QPainterPath &path, const QQuickPathData &) override;
Q_SIGNALS:
@@ -308,12 +321,14 @@ Q_SIGNALS:
void radiusYChanged();
void useLargeArcChanged();
void directionChanged();
+ Q_REVISION(2) void xAxisRotationChanged();
private:
qreal _radiusX;
qreal _radiusY;
bool _useLargeArc;
ArcDirection _direction;
+ qreal _xAxisRotation;
};
class Q_QUICK_PRIVATE_EXPORT QQuickPathSvg : public QQuickCurve
@@ -404,6 +419,7 @@ Q_SIGNALS:
void startYChanged();
protected:
+ QQuickPath(QQuickPathPrivate &dd, QObject *parent = nullptr);
void componentComplete() override;
void classBegin() override;
void disconnectPathElements();
@@ -458,6 +474,7 @@ QML_DECLARE_TYPE(QQuickPathElement)
QML_DECLARE_TYPE(QQuickPathAttribute)
QML_DECLARE_TYPE(QQuickCurve)
QML_DECLARE_TYPE(QQuickPathLine)
+QML_DECLARE_TYPE(QQuickPathMove)
QML_DECLARE_TYPE(QQuickPathQuad)
QML_DECLARE_TYPE(QQuickPathCubic)
QML_DECLARE_TYPE(QQuickPathCatmullRomCurve)
diff --git a/src/quick/util/qquickpath_p_p.h b/src/quick/util/qquickpath_p_p.h
index 1dc3c1c47a..8ce85dbf0f 100644
--- a/src/quick/util/qquickpath_p_p.h
+++ b/src/quick/util/qquickpath_p_p.h
@@ -64,7 +64,7 @@ QT_REQUIRE_CONFIG(quick_path);
QT_BEGIN_NAMESPACE
-class QQuickPathPrivate : public QObjectPrivate
+class Q_QUICK_PRIVATE_EXPORT QQuickPathPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QQuickPath)
diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp
index 882482ad31..e218b84fff 100644
--- a/src/quick/util/qquickpixmapcache.cpp
+++ b/src/quick/util/qquickpixmapcache.cpp
@@ -49,6 +49,7 @@
#include <qpa/qplatformintegration.h>
#include <QtQuick/private/qsgtexture_p.h>
+#include <QtQuick/private/qsgtexturereader_p.h>
#include <QQuickWindow>
#include <QCoreApplication>
@@ -162,7 +163,7 @@ Q_SIGNALS:
void downloadProgress(qint64, qint64);
protected:
- bool event(QEvent *event);
+ bool event(QEvent *event) override;
private:
Q_DISABLE_COPY(QQuickPixmapReply)
@@ -200,7 +201,7 @@ public:
static QQuickPixmapReader *existingInstance(QQmlEngine *engine);
protected:
- void run();
+ void run() override;
private:
friend class QQuickPixmapReaderThreadObject;
@@ -771,8 +772,26 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
QFile f(localFile);
QSize readSize;
if (f.open(QIODevice::ReadOnly)) {
- if (!readImage(url, &f, &image, &errorStr, &readSize, runningJob->requestSize, runningJob->providerOptions))
- errorCode = QQuickPixmapReply::Loading;
+
+ // for now, purely use suffix information to determine whether we are working with a compressed texture
+ QByteArray suffix = QFileInfo(f).suffix().toLower().toLatin1();
+ if (QSGTextureReader::isTexture(&f, suffix)) {
+ QQuickTextureFactory *factory = QSGTextureReader::read(&f, suffix);
+ if (factory) {
+ readSize = factory->textureSize();
+ } else {
+ errorStr = QQuickPixmap::tr("Error decoding: %1").arg(url.toString());
+ errorCode = QQuickPixmapReply::Decoding;
+ }
+ mutex.lock();
+ if (!cancelled.contains(runningJob))
+ runningJob->postReply(errorCode, errorStr, readSize, factory);
+ mutex.unlock();
+ return;
+ } else {
+ if (!readImage(url, &f, &image, &errorStr, &readSize, runningJob->requestSize, runningJob->providerOptions))
+ errorCode = QQuickPixmapReply::Loading;
+ }
} else {
errorStr = QQuickPixmap::tr("Cannot open: %1").arg(url.toString());
errorCode = QQuickPixmapReply::Loading;
@@ -1233,11 +1252,23 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q
QString errorString;
if (f.open(QIODevice::ReadOnly)) {
- QImage image;
- QQuickImageProviderOptions::AutoTransform appliedTransform = providerOptions.autoTransform();
- if (readImage(url, &f, &image, &errorString, &readSize, requestSize, providerOptions, &appliedTransform)) {
- *ok = true;
- return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, providerOptions, appliedTransform);
+ // for now, purely use suffix information to determine whether we are working with a compressed texture
+ QByteArray suffix = QFileInfo(f).suffix().toLower().toLatin1();
+ if (QSGTextureReader::isTexture(&f, suffix)) {
+ QQuickTextureFactory *factory = QSGTextureReader::read(&f, suffix);
+ if (factory) {
+ *ok = true;
+ return new QQuickPixmapData(declarativePixmap, factory);
+ } else {
+ errorString = QQuickPixmap::tr("Error decoding: %1").arg(url.toString());
+ }
+ } else {
+ QImage image;
+ QQuickImageProviderOptions::AutoTransform appliedTransform = providerOptions.autoTransform();
+ if (readImage(url, &f, &image, &errorString, &readSize, requestSize, providerOptions, &appliedTransform)) {
+ *ok = true;
+ return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, providerOptions, appliedTransform);
+ }
}
} else {
errorString = QQuickPixmap::tr("Cannot open: %1").arg(url.toString());
diff --git a/src/quick/util/qquicksvgparser.cpp b/src/quick/util/qquicksvgparser.cpp
index 310b600965..086c6d0b28 100644
--- a/src/quick/util/qquicksvgparser.cpp
+++ b/src/quick/util/qquicksvgparser.cpp
@@ -45,8 +45,6 @@
QT_BEGIN_NAMESPACE
-static const double Q_PI = 3.14159265358979323846; // pi
-
//copied from Qt SVG (qsvghandler.cpp).
Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
// '0' is 0x30 and '9' is 0x39
@@ -253,11 +251,11 @@ void QQuickSvgParser::pathArc(QPainterPath &path,
th_arc = th1 - th0;
if (th_arc < 0 && sweep_flag)
- th_arc += 2 * Q_PI;
+ th_arc += 2 * M_PI;
else if (th_arc > 0 && !sweep_flag)
- th_arc -= 2 * Q_PI;
+ th_arc -= 2 * M_PI;
- n_segs = qCeil(qAbs(th_arc / (Q_PI * 0.5 + 0.001)));
+ n_segs = qCeil(qAbs(th_arc / (M_PI * 0.5 + 0.001)));
for (i = 0; i < n_segs; i++) {
pathArcSegment(path, xc, yc,
diff --git a/src/quick/util/qquicksvgparser_p.h b/src/quick/util/qquicksvgparser_p.h
index 44b0d1b6dd..1777b99bf4 100644
--- a/src/quick/util/qquicksvgparser_p.h
+++ b/src/quick/util/qquicksvgparser_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
#include <QtCore/qstring.h>
#include <QtGui/qpainterpath.h>
@@ -59,9 +60,9 @@ QT_BEGIN_NAMESPACE
namespace QQuickSvgParser
{
bool parsePathDataFast(const QString &dataStr, QPainterPath &path);
- void pathArc(QPainterPath &path, qreal rx, qreal ry, qreal x_axis_rotation,
- int large_arc_flag, int sweep_flag, qreal x, qreal y, qreal curx,
- qreal cury);
+ Q_QUICK_PRIVATE_EXPORT void pathArc(QPainterPath &path, qreal rx, qreal ry, qreal x_axis_rotation,
+ int large_arc_flag, int sweep_flag, qreal x, qreal y, qreal curx,
+ qreal cury);
}
QT_END_NAMESPACE
diff --git a/src/quick/util/qquickvaluetypes.cpp b/src/quick/util/qquickvaluetypes.cpp
index 4d34c6d661..bc4a72b6ea 100644
--- a/src/quick/util/qquickvaluetypes.cpp
+++ b/src/quick/util/qquickvaluetypes.cpp
@@ -757,6 +757,16 @@ void QQuickFontValueType::setHintingPreference(QQuickFontValueType::HintingPrefe
v.setHintingPreference(QFont::HintingPreference(hintingPreference));
}
+bool QQuickFontValueType::kerning() const
+{
+ return v.kerning();
+}
+
+void QQuickFontValueType::setKerning(bool b)
+{
+ v.setKerning(b);
+}
+
QT_END_NAMESPACE
#include "moc_qquickvaluetypes_p.cpp"
diff --git a/src/quick/util/qquickvaluetypes_p.h b/src/quick/util/qquickvaluetypes_p.h
index 4a1598ec5c..a3f35a84ec 100644
--- a/src/quick/util/qquickvaluetypes_p.h
+++ b/src/quick/util/qquickvaluetypes_p.h
@@ -323,6 +323,7 @@ class QQuickFontValueType
Q_PROPERTY(qreal letterSpacing READ letterSpacing WRITE setLetterSpacing FINAL)
Q_PROPERTY(qreal wordSpacing READ wordSpacing WRITE setWordSpacing FINAL)
Q_PROPERTY(HintingPreference hintingPreference READ hintingPreference WRITE setHintingPreference FINAL)
+ Q_PROPERTY(bool kerning READ kerning WRITE setKerning FINAL)
public:
enum FontWeight { Thin = QFont::Thin,
@@ -393,6 +394,9 @@ public:
HintingPreference hintingPreference() const;
void setHintingPreference(HintingPreference);
+
+ bool kerning() const;
+ void setKerning(bool b);
};
QT_END_NAMESPACE