aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick')
-rw-r--r--src/quick/CMakeLists.txt3
-rw-r--r--src/quick/accessible/qaccessiblequickitem.cpp7
-rw-r--r--src/quick/designer/qquickdesignersupport.cpp14
-rw-r--r--src/quick/designer/qquickdesignersupport_p.h3
-rw-r--r--src/quick/designer/qquickdesignersupportitems.cpp1
-rw-r--r--src/quick/designer/qquickdesignerwindowmanager.cpp125
-rw-r--r--src/quick/designer/qquickdesignerwindowmanager_p.h108
-rw-r--r--src/quick/doc/images/pointerHandlers/tapHandlerButtonReleaseWithinBounds.webpbin0 -> 53154 bytes
-rw-r--r--src/quick/doc/images/pointerHandlers/tapHandlerButtonWithinBounds.webpbin0 -> 27426 bytes
-rw-r--r--src/quick/doc/images/pointerHandlers/tapHandlerOverlappingButtons.webpbin0 -> 51008 bytes
-rw-r--r--src/quick/doc/qtquick.qdocconf49
-rw-r--r--src/quick/doc/snippets/cmake-macros/examples.cmake6
-rw-r--r--src/quick/doc/snippets/code/doc_src_qtquick.cmake2
-rw-r--r--src/quick/doc/snippets/pointerHandlers/hoverModifiers.qml69
-rw-r--r--src/quick/doc/snippets/pointerHandlers/hoverMouseOrStylus.qml69
-rw-r--r--src/quick/doc/snippets/pointerHandlers/hoverStylusOrEraser.qml76
-rw-r--r--src/quick/doc/snippets/pointerHandlers/hoverTapKeyButton.qml73
-rw-r--r--src/quick/doc/snippets/pointerHandlers/images/cursor-eraser.pngbin0 -> 1454 bytes
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pointHandler.qml5
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedButtons.qml68
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedModifiers.qml83
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pointHandlerCanvasDrawing.qml101
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pointHandlerMargin.qml83
-rw-r--r--src/quick/doc/snippets/pointerHandlers/tapHandlerButton.qml80
-rw-r--r--src/quick/doc/snippets/pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml98
-rw-r--r--src/quick/doc/snippets/pointerHandlers/tapHandlerButtonWithinBounds.qml98
-rw-r--r--src/quick/doc/snippets/pointerHandlers/tapHandlerOnTapped.qml8
-rw-r--r--src/quick/doc/snippets/pointerHandlers/tapHandlerOverlappingButtons.qml98
-rw-r--r--src/quick/doc/src/advtutorial.qdoc31
-rw-r--r--src/quick/doc/src/cmake-macros.qdoc56
-rw-r--r--src/quick/doc/src/concepts/effects/particles.qdoc2
-rw-r--r--src/quick/doc/src/concepts/input/focus.qdoc9
-rw-r--r--src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc67
-rw-r--r--src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc9
-rw-r--r--src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc2
-rw-r--r--src/quick/doc/src/concepts/positioning/righttoleft.qdoc14
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc1
-rw-r--r--src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc220
-rw-r--r--src/quick/doc/src/guidelines/qtquick-tool-qmllint.qdoc43
-rw-r--r--src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc74
-rw-r--r--src/quick/doc/src/qmltypereference.qdoc2
-rw-r--r--src/quick/doc/src/qt6-changes.qdoc4
-rw-r--r--src/quick/doc/src/qtquick.qdoc1
-rw-r--r--src/quick/doc/src/tutorial.qdoc10
-rw-r--r--src/quick/handlers/qquickdragaxis.cpp2
-rw-r--r--src/quick/handlers/qquickdraghandler.cpp14
-rw-r--r--src/quick/handlers/qquickhandlerpoint.cpp27
-rw-r--r--src/quick/handlers/qquickhoverhandler.cpp108
-rw-r--r--src/quick/handlers/qquickmultipointhandler.cpp6
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp20
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler.cpp4
-rw-r--r--src/quick/handlers/qquickpointerhandler.cpp53
-rw-r--r--src/quick/handlers/qquickpointhandler.cpp128
-rw-r--r--src/quick/handlers/qquicksinglepointhandler.cpp4
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp133
-rw-r--r--src/quick/handlers/qquicktaphandler_p.h7
-rw-r--r--src/quick/handlers/qquickwheelhandler.cpp8
-rw-r--r--src/quick/items/context2d/qquickcanvasitem.cpp6
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp31
-rw-r--r--src/quick/items/context2d/qquickcontext2d_p.h2
-rw-r--r--src/quick/items/context2d/qquickcontext2dtexture.cpp11
-rw-r--r--src/quick/items/qquickaccessibleattached.cpp7
-rw-r--r--src/quick/items/qquickaccessibleattached_p.h2
-rw-r--r--src/quick/items/qquickanchors.cpp2
-rw-r--r--src/quick/items/qquickanimatedsprite.cpp14
-rw-r--r--src/quick/items/qquickcolorgroup.cpp4
-rw-r--r--src/quick/items/qquickdrag.cpp12
-rw-r--r--src/quick/items/qquickdrag_p.h3
-rw-r--r--src/quick/items/qquickdroparea.cpp38
-rw-r--r--src/quick/items/qquickdroparea_p.h14
-rw-r--r--src/quick/items/qquickevents.cpp53
-rw-r--r--src/quick/items/qquickflickable.cpp197
-rw-r--r--src/quick/items/qquickflickable_p_p.h41
-rw-r--r--src/quick/items/qquickflipable.cpp54
-rw-r--r--src/quick/items/qquickgridview.cpp32
-rw-r--r--src/quick/items/qquickimage.cpp6
-rw-r--r--src/quick/items/qquickimagebase.cpp30
-rw-r--r--src/quick/items/qquickimplicitsizeitem_p.h2
-rw-r--r--src/quick/items/qquickitem.cpp338
-rw-r--r--src/quick/items/qquickitem.h1
-rw-r--r--src/quick/items/qquickitem_p.h28
-rw-r--r--src/quick/items/qquickitemchangelistener_p.h1
-rw-r--r--src/quick/items/qquickitemgrabresult.h2
-rw-r--r--src/quick/items/qquickitemsmodule.cpp136
-rw-r--r--src/quick/items/qquickitemview.cpp26
-rw-r--r--src/quick/items/qquickitemview_p_p.h5
-rw-r--r--src/quick/items/qquickitemviewtransition.cpp35
-rw-r--r--src/quick/items/qquickitemviewtransition_p.h3
-rw-r--r--src/quick/items/qquicklistview.cpp21
-rw-r--r--src/quick/items/qquickloader.cpp19
-rw-r--r--src/quick/items/qquickmousearea.cpp36
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp12
-rw-r--r--src/quick/items/qquickmultipointtoucharea_p.h3
-rw-r--r--src/quick/items/qquickpainteditem.cpp2
-rw-r--r--src/quick/items/qquickpalette.cpp2
-rw-r--r--src/quick/items/qquickpathview.cpp13
-rw-r--r--src/quick/items/qquickpathview_p_p.h6
-rw-r--r--src/quick/items/qquickpincharea.cpp28
-rw-r--r--src/quick/items/qquickpositioners.cpp6
-rw-r--r--src/quick/items/qquickrectangle.cpp9
-rw-r--r--src/quick/items/qquickrendercontrol.cpp23
-rw-r--r--src/quick/items/qquickrendercontrol.h3
-rw-r--r--src/quick/items/qquickrendercontrol_p.h3
-rw-r--r--src/quick/items/qquickscalegrid.cpp2
-rw-r--r--src/quick/items/qquickshadereffect.cpp23
-rw-r--r--src/quick/items/qquickshadereffectsource.cpp8
-rw-r--r--src/quick/items/qquickshadereffectsource_p.h1
-rw-r--r--src/quick/items/qquickspriteengine.cpp1
-rw-r--r--src/quick/items/qquickstateoperations.cpp92
-rw-r--r--src/quick/items/qquicktableview.cpp152
-rw-r--r--src/quick/items/qquicktableview_p_p.h16
-rw-r--r--src/quick/items/qquicktext.cpp92
-rw-r--r--src/quick/items/qquicktext_p.h2
-rw-r--r--src/quick/items/qquicktextedit.cpp13
-rw-r--r--src/quick/items/qquicktextedit_p.h2
-rw-r--r--src/quick/items/qquicktextinput.cpp60
-rw-r--r--src/quick/items/qquicktextinput_p_p.h2
-rw-r--r--src/quick/items/qquicktextnodeengine.cpp5
-rw-r--r--src/quick/items/qquickwindow.cpp132
-rw-r--r--src/quick/items/qquickwindow_p.h18
-rw-r--r--src/quick/items/qquickwindowmodule_p.h5
-rw-r--r--src/quick/qtquickglobal_p.h30
-rw-r--r--src/quick/qtquickplugin.cpp3
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp2
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp21
-rw-r--r--src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp2
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp308
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h32
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.cpp6
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.h1
-rw-r--r--src/quick/scenegraph/coreapi/qsgnode.cpp4
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer.cpp3
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer_p.h13
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode.cpp15
-rw-r--r--src/quick/scenegraph/coreapi/qsgtexture.cpp5
-rw-r--r--src/quick/scenegraph/coreapi/qsgtexture_mac.mm1
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h5
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.cpp63
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext.cpp29
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext_p.h6
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode.cpp82
-rw-r--r--src/quick/scenegraph/qsgdistancefieldglyphnode_p.h4
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp45
-rw-r--r--src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp66
-rw-r--r--src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h6
-rw-r--r--src/quick/scenegraph/qsgrhilayer.cpp8
-rw-r--r--src/quick/scenegraph/qsgrhishadereffectnode.cpp41
-rw-r--r--src/quick/scenegraph/qsgrhishadereffectnode_p.h3
-rw-r--r--src/quick/scenegraph/qsgrhitextureglyphcache.cpp52
-rw-r--r--src/quick/scenegraph/qsgrhitextureglyphcache_p.h7
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp60
-rw-r--r--src/quick/scenegraph/shaders_ng/24bittextmask.frag5
-rw-r--r--src/quick/scenegraph/shaders_ng/32bitcolortext.frag5
-rw-r--r--src/quick/scenegraph/shaders_ng/8bittextmask.frag3
-rw-r--r--src/quick/scenegraph/shaders_ng/8bittextmask_a.frag3
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext.frag5
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext.vert9
-rw-r--r--src/quick/scenegraph/shaders_ng/outlinedtext_a.frag5
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext.frag3
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext.vert7
-rw-r--r--src/quick/scenegraph/shaders_ng/styledtext_a.frag3
-rw-r--r--src/quick/scenegraph/shaders_ng/textmask.frag3
-rw-r--r--src/quick/scenegraph/shaders_ng/textmask.vert7
-rw-r--r--src/quick/scenegraph/util/qsgdefaultpainternode.cpp12
-rw-r--r--src/quick/scenegraph/util/qsgplaintexture.cpp9
-rw-r--r--src/quick/scenegraph/util/qsgrhiatlastexture.cpp4
-rw-r--r--src/quick/scenegraph/util/qsgtexturematerial.cpp4
-rw-r--r--src/quick/util/qquickanimation.cpp5
-rw-r--r--src/quick/util/qquickanimation_p_p.h2
-rw-r--r--src/quick/util/qquickanimatorjob.cpp16
-rw-r--r--src/quick/util/qquickanimatorjob_p.h11
-rw-r--r--src/quick/util/qquickapplication.cpp172
-rw-r--r--src/quick/util/qquickbehavior.cpp7
-rw-r--r--src/quick/util/qquickdeliveryagent.cpp261
-rw-r--r--src/quick/util/qquickdeliveryagent_p_p.h11
-rw-r--r--src/quick/util/qquickfontmetrics.cpp16
-rw-r--r--src/quick/util/qquickimageprovider.cpp15
-rw-r--r--src/quick/util/qquickpixmapcache.cpp16
-rw-r--r--src/quick/util/qquickpixmapcache_p.h6
-rw-r--r--src/quick/util/qquickpropertychanges.cpp35
-rw-r--r--src/quick/util/qquicksmoothedanimation.cpp2
-rw-r--r--src/quick/util/qquickstatechangescript.cpp4
-rw-r--r--src/quick/util/qquickstategroup.cpp14
-rw-r--r--src/quick/util/qquickstyledtext.cpp4
-rw-r--r--src/quick/util/qquicktimeline.cpp2
-rw-r--r--src/quick/util/qquicktransition.cpp11
-rw-r--r--src/quick/util/qquicktransition_p.h1
-rw-r--r--src/quick/util/qquickvalidator.cpp6
188 files changed, 4148 insertions, 1813 deletions
diff --git a/src/quick/CMakeLists.txt b/src/quick/CMakeLists.txt
index 2c2324d896..372df6cfd8 100644
--- a/src/quick/CMakeLists.txt
+++ b/src/quick/CMakeLists.txt
@@ -237,7 +237,7 @@ qt_internal_add_shaders(Quick "scenegraph_shaders"
PRECOMPILE
OPTIMIZED
PREFIX
- "/qt-project.org/scenegraph/shaders_ng"
+ "/qt-project.org"
FILES
"scenegraph/shaders_ng/24bittextmask.frag"
"scenegraph/shaders_ng/32bitcolortext.frag"
@@ -345,7 +345,6 @@ qt_internal_extend_target(Quick CONDITION QT_FEATURE_quick_designer
designer/qquickdesignersupportproperties.cpp designer/qquickdesignersupportproperties_p.h
designer/qquickdesignersupportpropertychanges.cpp designer/qquickdesignersupportpropertychanges_p.h
designer/qquickdesignersupportstates.cpp designer/qquickdesignersupportstates_p.h
- designer/qquickdesignerwindowmanager.cpp designer/qquickdesignerwindowmanager_p.h
)
qt_internal_extend_target(Quick CONDITION QT_FEATURE_accessibility
diff --git a/src/quick/accessible/qaccessiblequickitem.cpp b/src/quick/accessible/qaccessiblequickitem.cpp
index 933a5b5cbe..9a4ce960f1 100644
--- a/src/quick/accessible/qaccessiblequickitem.cpp
+++ b/src/quick/accessible/qaccessiblequickitem.cpp
@@ -448,6 +448,11 @@ QList<QQuickItem *> QAccessibleQuickItem::childItems() const
return accessibleUnignoredChildren(item());
}
+static bool isTextRole(QAccessible::Role role)
+{
+ return role == QAccessible::EditableText || role == QAccessible::StaticText;
+}
+
QAccessible::State QAccessibleQuickItem::state() const
{
QQuickAccessibleAttached *attached = QQuickAccessibleAttached::attachedProperties(item());
@@ -465,7 +470,7 @@ QAccessible::State QAccessibleQuickItem::state() const
state.offscreen = true;
if ((role() == QAccessible::CheckBox || role() == QAccessible::RadioButton) && object()->property("checked").toBool())
state.checked = true;
- if (item()->activeFocusOnTab() || role() == QAccessible::EditableText)
+ if (item()->activeFocusOnTab() || isTextRole(role()))
state.focusable = true;
if (item()->hasActiveFocus())
state.focused = true;
diff --git a/src/quick/designer/qquickdesignersupport.cpp b/src/quick/designer/qquickdesignersupport.cpp
index fe9bb945b4..24b22a2d0f 100644
--- a/src/quick/designer/qquickdesignersupport.cpp
+++ b/src/quick/designer/qquickdesignersupport.cpp
@@ -47,7 +47,6 @@
#include <QtQml/private/qabstractanimationjob_p.h>
#include <private/qqmlengine_p.h>
#include <private/qquickview_p.h>
-#include <private/qsgrenderloop_p.h>
#include <QtQuick/private/qquickstategroup_p.h>
#include <QtGui/QImage>
#include <private/qqmlvme_p.h>
@@ -55,9 +54,6 @@
#include <private/qqmldata_p.h>
#include <private/qsgadaptationlayer_p.h>
-#include "qquickdesignerwindowmanager_p.h"
-
-
QT_BEGIN_NAMESPACE
QQuickDesignerSupport::QQuickDesignerSupport()
@@ -453,11 +449,6 @@ void QQuickDesignerSupport::updateDirtyNode(QQuickItem *item)
QQuickWindowPrivate::get(item->window())->updateDirtyNode(item);
}
-void QQuickDesignerSupport::activateDesignerWindowManager()
-{
- QSGRenderLoop::setInstance(new QQuickDesignerWindowManager);
-}
-
void QQuickDesignerSupport::activateDesignerMode()
{
QQmlEnginePrivate::activateDesignerMode();
@@ -473,11 +464,6 @@ void QQuickDesignerSupport::enableComponentComplete()
QQmlVME::enableComponentComplete();
}
-void QQuickDesignerSupport::createOpenGLContext(QQuickWindow *window)
-{
- QQuickDesignerWindowManager::createOpenGLContext(window);
-}
-
void QQuickDesignerSupport::polishItems(QQuickWindow *window)
{
QQuickWindowPrivate::get(window)->polishItems();
diff --git a/src/quick/designer/qquickdesignersupport_p.h b/src/quick/designer/qquickdesignersupport_p.h
index fc46745e15..6d2da6aa35 100644
--- a/src/quick/designer/qquickdesignersupport_p.h
+++ b/src/quick/designer/qquickdesignersupport_p.h
@@ -143,14 +143,11 @@ public:
static void updateDirtyNode(QQuickItem *item);
- static void activateDesignerWindowManager();
static void activateDesignerMode();
static void disableComponentComplete();
static void enableComponentComplete();
- static void createOpenGLContext(QQuickWindow *window);
-
static void polishItems(QQuickWindow *window);
private:
diff --git a/src/quick/designer/qquickdesignersupportitems.cpp b/src/quick/designer/qquickdesignersupportitems.cpp
index 862b57f2b7..8a45209363 100644
--- a/src/quick/designer/qquickdesignersupportitems.cpp
+++ b/src/quick/designer/qquickdesignersupportitems.cpp
@@ -72,7 +72,6 @@ static void stopAnimation(QObject *object)
// QQuickScriptAction *scriptAimation = qobject_cast<QQuickScriptAction*>(animation);
// if (scriptAimation) FIXME
// scriptAimation->setScript(QQmlScriptString());
- animation->setLoops(1);
animation->complete();
animation->setDisableUserControl();
} else if (timer) {
diff --git a/src/quick/designer/qquickdesignerwindowmanager.cpp b/src/quick/designer/qquickdesignerwindowmanager.cpp
deleted file mode 100644
index 18daab70e8..0000000000
--- a/src/quick/designer/qquickdesignerwindowmanager.cpp
+++ /dev/null
@@ -1,125 +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 "qquickdesignerwindowmanager_p.h"
-#include "private/qquickwindow_p.h"
-#include "private/qquickitem_p.h"
-#include <QtQuick/QQuickWindow>
-#if QT_CONFIG(opengl)
-#include <private/qsgdefaultrendercontext_p.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-QQuickDesignerWindowManager::QQuickDesignerWindowManager()
- : m_sgContext(QSGContext::createDefaultContext())
-{
- m_renderContext.reset(m_sgContext.data()->createRenderContext());
-}
-
-void QQuickDesignerWindowManager::show(QQuickWindow *window)
-{
- makeOpenGLContext(window);
-}
-
-void QQuickDesignerWindowManager::hide(QQuickWindow *)
-{
-}
-
-void QQuickDesignerWindowManager::windowDestroyed(QQuickWindow *)
-{
-}
-
-void QQuickDesignerWindowManager::makeOpenGLContext(QQuickWindow *window)
-{
-#if QT_CONFIG(opengl)
- if (!m_openGlContext) {
- m_openGlContext.reset(new QOpenGLContext());
- m_openGlContext->setFormat(window->requestedFormat());
- m_openGlContext->create();
- if (!m_openGlContext->makeCurrent(window))
- qWarning("QQuickWindow: makeCurrent() failed...");
- QSGDefaultRenderContext::InitParams params;
- params.sampleCount = qMax(1, m_openGlContext->format().samples());
- // ### glpurge Designer's RHI migration is left as a future exercise (QTBUG-78598)
- //params.openGLContext = m_openGlContext.data();
- params.initialSurfacePixelSize = window->size() * window->effectiveDevicePixelRatio();
- params.maybeSurface = window;
- m_renderContext->initialize(&params);
- } else {
- m_openGlContext->makeCurrent(window);
- }
-#else
- Q_UNUSED(window);
-#endif
-}
-
-void QQuickDesignerWindowManager::exposureChanged(QQuickWindow *)
-{
-}
-
-QImage QQuickDesignerWindowManager::grab(QQuickWindow *)
-{
- return QImage();
-}
-
-void QQuickDesignerWindowManager::maybeUpdate(QQuickWindow *)
-{
-}
-
-QSGContext *QQuickDesignerWindowManager::sceneGraphContext() const
-{
- return m_sgContext.data();
-}
-
-void QQuickDesignerWindowManager::createOpenGLContext(QQuickWindow *window)
-{
- window->create();
- window->update();
-}
-
-void QQuickDesignerWindowManager::update(QQuickWindow *window)
-{
- makeOpenGLContext(window);
-}
-
-QT_END_NAMESPACE
-
-
-#include "moc_qquickdesignerwindowmanager_p.cpp"
diff --git a/src/quick/designer/qquickdesignerwindowmanager_p.h b/src/quick/designer/qquickdesignerwindowmanager_p.h
deleted file mode 100644
index ac9695953f..0000000000
--- a/src/quick/designer/qquickdesignerwindowmanager_p.h
+++ /dev/null
@@ -1,108 +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$
-**
-****************************************************************************/
-
-#ifndef DESIGNERWINDOWMANAGER_P_H
-#define DESIGNERWINDOWMANAGER_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/QScopedPointer>
-
-#include <private/qsgrenderloop_p.h>
-#include <private/qtquickglobal_p.h>
-#include <private/qsgcontext_p.h>
-
-#if QT_CONFIG(opengl)
-# include <QOpenGLContext>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class QQuickWindow;
-class QSGContext;
-class QSGDefaultRenderContext;
-class QAnimationDriver;
-
-class QQuickDesignerWindowManager : public QSGRenderLoop
-{
- Q_OBJECT
-public:
- QQuickDesignerWindowManager();
-
- void show(QQuickWindow *window) override;
- void hide(QQuickWindow *window) override;
-
- void windowDestroyed(QQuickWindow *window) override;
-
- void makeOpenGLContext(QQuickWindow *window);
- void exposureChanged(QQuickWindow *window) override;
- QImage grab(QQuickWindow *window) override;
-
- void maybeUpdate(QQuickWindow *window) override;
- void update(QQuickWindow *window) override; // identical for this implementation.
-
- void releaseResources(QQuickWindow *) override { }
-
- QAnimationDriver *animationDriver() const override { return nullptr; }
-
- QSGContext *sceneGraphContext() const override;
- QSGRenderContext *createRenderContext(QSGContext *) const override { return m_renderContext.data(); }
-
- static void createOpenGLContext(QQuickWindow *window);
-
-private:
-#if QT_CONFIG(opengl)
- QScopedPointer<QOpenGLContext> m_openGlContext;
-#endif
- QScopedPointer<QSGContext> m_sgContext;
- QScopedPointer<QSGRenderContext> m_renderContext;
-};
-
-QT_END_NAMESPACE
-
-#endif // DESIGNERWINDOWMANAGER_P_H
diff --git a/src/quick/doc/images/pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp b/src/quick/doc/images/pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp
new file mode 100644
index 0000000000..3edab7d7a1
--- /dev/null
+++ b/src/quick/doc/images/pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp
Binary files differ
diff --git a/src/quick/doc/images/pointerHandlers/tapHandlerButtonWithinBounds.webp b/src/quick/doc/images/pointerHandlers/tapHandlerButtonWithinBounds.webp
new file mode 100644
index 0000000000..05cb2f2276
--- /dev/null
+++ b/src/quick/doc/images/pointerHandlers/tapHandlerButtonWithinBounds.webp
Binary files differ
diff --git a/src/quick/doc/images/pointerHandlers/tapHandlerOverlappingButtons.webp b/src/quick/doc/images/pointerHandlers/tapHandlerOverlappingButtons.webp
new file mode 100644
index 0000000000..0455097159
--- /dev/null
+++ b/src/quick/doc/images/pointerHandlers/tapHandlerOverlappingButtons.webp
Binary files differ
diff --git a/src/quick/doc/qtquick.qdocconf b/src/quick/doc/qtquick.qdocconf
index 7875b5bd8e..ff4fbbc38d 100644
--- a/src/quick/doc/qtquick.qdocconf
+++ b/src/quick/doc/qtquick.qdocconf
@@ -30,13 +30,13 @@ qhp.QtQuick.subprojects.qmltypes.sortPages = true
qhp.QtQuick.subprojects.classes.title = Classes
qhp.QtQuick.subprojects.classes.title = C++ Classes
qhp.QtQuick.subprojects.classes.indexTitle = Qt Quick C++ Classes
-qhp.QtQuick.subprojects.classes.selectors = class fake:headerfile
+qhp.QtQuick.subprojects.classes.selectors = class doc:headerfile
qhp.QtQuick.subprojects.classes.sortPages = true
qhp.QtQuick.subprojects.examples.title = Examples
qhp.QtQuick.subprojects.examples.indexTitle = Qt Quick Examples and Tutorials
-qhp.QtQuick.subprojects.examples.selectors = fake:example
+qhp.QtQuick.subprojects.examples.selectors = doc:example
-tagfile = ../../../doc/qtquick/qtquick.tags
+tagfile = qtquick.tags
depends += \
qtcore \
@@ -58,35 +58,37 @@ depends += \
qtcmake
{headerdirs,sourcedirs} += \
- ..\
+ .. \
../../quick \
../../quickwidgets \
../../qmllocalstorage \
../../quicklayouts \
- ../../labs/animation \
- ../../labs/folderlistmodel
+ ../../labs
-exampledirs += ../../../examples/quick \
- snippets
+# both have their own documentation project
+excludedirs += \
+ ../../labs/platform \
+ ../../labs/models
-imagedirs += images
+exampledirs += \
+ ../../../examples/quick \
+ ../../qmlmodels/doc/snippets \
+ snippets
-#add particles and shapes sources
-headerdirs += ../../particles \
- ../../quickshapes
-sourcedirs += ../../particles \
- ../../quickshapes
+imagedirs += images
-#add imports directory because of dependencies
-headerdirs += ../../imports
-sourcedirs += ../../imports
+# Add particles and shapes sources
+{headerdirs,sourcedirs} += \
+ ../../particles \
+ ../../quickshapes
-#add plugins directory because of dependencies
-headerdirs += ../../plugins
-sourcedirs += ../../plugins
+# Add imports and plugins directories because of dependencies
+{headerdirs,sourcedirs} += \
+ ../../imports \
+ ../../plugins
excludefiles += ../util/qquickpropertychanges_p.h
-examples.fileextensions += "*.qm"
+examples.fileextensions += "*.qm"
manifestmeta.thumbnail.names += "QtQuick/Threaded ListModel Example" \
"QtQuick/QML Dynamic View Ordering Tutorial*"
@@ -100,6 +102,5 @@ navigation.qmltypespage = "Qt Quick QML Types"
# \svgcolor {#ffdead}
macro.svgcolor.HTML = "<div style=\"padding:10px;color:#fff;background:\1;\"></div>"
-# youtube video thumbnails that show up in offline and online docs
-HTML.extraimages += images/9BcAYDlpuT8.jpg
-qhp.QtQuick.extraFiles += images/9BcAYDlpuT8.jpg
+# YouTube video thumbnail that show up in offline docs
+{HTML.extraimages,qhp.QtQuick.extraFiles} += images/9BcAYDlpuT8.jpg
diff --git a/src/quick/doc/snippets/cmake-macros/examples.cmake b/src/quick/doc/snippets/cmake-macros/examples.cmake
deleted file mode 100644
index cecc59debc..0000000000
--- a/src/quick/doc/snippets/cmake-macros/examples.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-#! [qt5_import_qml_plugins]
-find_package(Qt5 COMPONENTS Quick QmlImportScanner)
-add_executable(myapp main.cpp)
-target_link_libraries(myapp Qt5::Quick)
-qt5_import_qml_plugins(myapp)
-#! [qt5_import_qml_plugins]
diff --git a/src/quick/doc/snippets/code/doc_src_qtquick.cmake b/src/quick/doc/snippets/code/doc_src_qtquick.cmake
index f575b40acc..733145fdc5 100644
--- a/src/quick/doc/snippets/code/doc_src_qtquick.cmake
+++ b/src/quick/doc/snippets/code/doc_src_qtquick.cmake
@@ -1,4 +1,4 @@
#! [0]
-find_package(Qt6 COMPONENTS Quick REQUIRED)
+find_package(Qt6 REQUIRED COMPONENTS Quick)
target_link_libraries(mytarget PRIVATE Qt6::Quick)
#! [0]
diff --git a/src/quick/doc/snippets/pointerHandlers/hoverModifiers.qml b/src/quick/doc/snippets/pointerHandlers/hoverModifiers.qml
new file mode 100644
index 0000000000..b8a378ddcf
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/hoverModifiers.qml
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Rectangle {
+ width: 150; height: 50; radius: 3
+ color: control.hovered ? "goldenrod" : shift.hovered ? "wheat" : "beige"
+
+ HoverHandler {
+ id: control
+ acceptedModifiers: Qt.ControlModifier
+ cursorShape: Qt.PointingHandCursor
+ }
+
+ HoverHandler {
+ id: shift
+ acceptedModifiers: Qt.ShiftModifier
+ cursorShape: Qt.CrossCursor
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/hoverMouseOrStylus.qml b/src/quick/doc/snippets/pointerHandlers/hoverMouseOrStylus.qml
new file mode 100644
index 0000000000..6bd6a40b1a
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/hoverMouseOrStylus.qml
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Rectangle {
+ width: 150; height: 50; radius: 3
+ color: mouse.hovered ? "goldenrod" : stylus.hovered ? "tomato" : "wheat"
+
+ HoverHandler {
+ id: stylus
+ acceptedDevices: PointerDevice.Stylus
+ cursorShape: Qt.CrossCursor
+ }
+
+ HoverHandler {
+ id: mouse
+ acceptedDevices: PointerDevice.Mouse
+ cursorShape: Qt.PointingHandCursor
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/hoverStylusOrEraser.qml b/src/quick/doc/snippets/pointerHandlers/hoverStylusOrEraser.qml
new file mode 100644
index 0000000000..5d82158baf
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/hoverStylusOrEraser.qml
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Rectangle {
+ id: rect
+ width: 150; height: 150
+
+ HoverHandler {
+ id: stylus
+ acceptedPointerTypes: PointerDevice.Pen
+ cursorShape: Qt.CrossCursor
+ }
+
+ HoverHandler {
+ id: eraser
+ acceptedPointerTypes: PointerDevice.Eraser
+ cursorShape: Qt.BlankCursor
+ target: Image {
+ parent: rect
+ source: "images/cursor-eraser.png"
+ visible: eraser.hovered
+ x: eraser.point.position.x
+ y: eraser.point.position.y - 32
+ }
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/hoverTapKeyButton.qml b/src/quick/doc/snippets/pointerHandlers/hoverTapKeyButton.qml
new file mode 100644
index 0000000000..1564aa16b5
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/hoverTapKeyButton.qml
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick 2.12
+
+Rectangle {
+ id: button
+ signal clicked
+
+ width: 150; height: 50; radius: 3
+ color: tapHandler.pressed ? "goldenrod" : hoverHandler.hovered ? "wheat" : "beige"
+ border.color: activeFocus ? "brown" : "transparent"
+ focus: true
+
+ HoverHandler {
+ id: hoverHandler
+ }
+
+ TapHandler {
+ id: tapHandler
+ onTapped: button.clicked()
+ }
+
+ Keys.onEnterPressed: button.clicked()
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/images/cursor-eraser.png b/src/quick/doc/snippets/pointerHandlers/images/cursor-eraser.png
new file mode 100644
index 0000000000..e5488a89f2
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/images/cursor-eraser.png
Binary files differ
diff --git a/src/quick/doc/snippets/pointerHandlers/pointHandler.qml b/src/quick/doc/snippets/pointerHandlers/pointHandler.qml
index 20be120120..138ad332d9 100644
--- a/src/quick/doc/snippets/pointerHandlers/pointHandler.qml
+++ b/src/quick/doc/snippets/pointerHandlers/pointHandler.qml
@@ -48,8 +48,7 @@
**
****************************************************************************/
//![0]
-import QtQuick 2.12
-import QtQuick.Window 2.2
+import QtQuick
Window {
width: 480
@@ -61,6 +60,7 @@ Window {
z: 10000
anchors.fill: parent
+ //![1]
PointHandler {
id: handler
acceptedDevices: PointerDevice.TouchScreen | PointerDevice.TouchPad
@@ -73,6 +73,7 @@ Window {
width: 20; height: width; radius: width / 2
}
}
+ //![1]
}
}
//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedButtons.qml b/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedButtons.qml
new file mode 100644
index 0000000000..a884672b0d
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedButtons.qml
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Item {
+ width: 480; height: 320
+
+ Rectangle {
+ color: handler.active ? "tomato" : "wheat"
+ x: handler.point.position.x - width / 2
+ y: handler.point.position.y - height / 2
+ width: 20; height: width; radius: width / 2
+ }
+
+ PointHandler {
+ id: handler
+ acceptedButtons: Qt.MiddleButton | Qt.RightButton
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedModifiers.qml b/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedModifiers.qml
new file mode 100644
index 0000000000..0229f3bb0a
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/pointHandlerAcceptedModifiers.qml
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Item {
+ id: feedbackPane
+ width: 480; height: 320
+
+ PointHandler {
+ id: control
+ acceptedModifiers: Qt.ControlModifier
+ cursorShape: Qt.PointingHandCursor
+ target: Rectangle {
+ parent: feedbackPane
+ color: control.active ? "indianred" : "khaki"
+ x: control.point.position.x - width / 2
+ y: control.point.position.y - height / 2
+ width: 20; height: width; radius: width / 2
+ }
+ }
+
+ PointHandler {
+ id: shift
+ acceptedModifiers: Qt.ShiftModifier | Qt.MetaModifier
+ cursorShape: Qt.CrossCursor
+ target: Rectangle {
+ parent: feedbackPane
+ color: shift.active ? "darkslateblue" : "lightseagreen"
+ x: shift.point.position.x - width / 2
+ y: shift.point.position.y - height / 2
+ width: 30; height: width; radius: width / 2
+ }
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/pointHandlerCanvasDrawing.qml b/src/quick/doc/snippets/pointerHandlers/pointHandlerCanvasDrawing.qml
new file mode 100644
index 0000000000..5bc98c1230
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/pointHandlerCanvasDrawing.qml
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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$
+**
+****************************************************************************/
+
+//![0]
+import QtQuick
+
+Canvas {
+ id: canvas
+ width: 800
+ height: 600
+ antialiasing: true
+ renderTarget: Canvas.FramebufferObject
+ property var points: []
+ onPaint: {
+ if (points.length < 2)
+ return
+ var ctx = canvas.getContext('2d');
+ ctx.save()
+ ctx.strokeStyle = stylusHandler.active ? "blue" : "white"
+ ctx.lineCap = "round"
+ ctx.beginPath()
+ ctx.moveTo(points[0].x, points[0].y)
+ for (var i = 1; i < points.length; i++)
+ ctx.lineTo(points[i].x, points[i].y)
+ ctx.lineWidth = 3
+ ctx.stroke()
+ points = points.slice(points.length - 2, 1)
+ ctx.restore()
+ }
+
+ PointHandler {
+ id: stylusHandler
+ acceptedPointerTypes: PointerDevice.Pen
+ onPointChanged: {
+ canvas.points.push(point.position)
+ canvas.requestPaint()
+ }
+ }
+
+ PointHandler {
+ id: eraserHandler
+ acceptedPointerTypes: PointerDevice.Eraser
+ onPointChanged: {
+ canvas.points.push(point.position)
+ canvas.requestPaint()
+ }
+ }
+
+ Rectangle {
+ width: 10; height: 10
+ color: stylusHandler.active ? "green" : eraserHandler.active ? "red" : "beige"
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/pointHandlerMargin.qml b/src/quick/doc/snippets/pointerHandlers/pointHandlerMargin.qml
new file mode 100644
index 0000000000..49161f0abe
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/pointHandlerMargin.qml
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Item {
+ width: 480; height: 320
+
+ Rectangle {
+ anchors.fill: handlingContainer
+ anchors.margins: -handler.margin
+ color: "beige"
+ }
+
+ Rectangle {
+ id: handlingContainer
+ width: 200; height: 200
+ anchors.centerIn: parent
+ border.color: "green"
+ color: handler.active ? "lightsteelblue" : "khaki"
+
+ Text {
+ text: "X"
+ x: handler.point.position.x - width / 2
+ y: handler.point.position.y - height / 2
+ visible: handler.active
+ }
+
+ PointHandler {
+ id: handler
+ margin: 30
+ }
+ }
+
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/tapHandlerButton.qml b/src/quick/doc/snippets/pointerHandlers/tapHandlerButton.qml
new file mode 100644
index 0000000000..02d0b4213b
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/tapHandlerButton.qml
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Rectangle {
+ id: button
+ signal clicked
+ property alias text: buttonLabel.text
+
+ height: Math.max(Screen.pixelDensity * 7, buttonLabel.implicitHeight * 1.2)
+ width: Math.max(Screen.pixelDensity * 11, buttonLabel.implicitWidth * 1.3)
+ radius: 3
+ property color dark: Qt.darker(palette.button, 1.3)
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: tapHandler.pressed ? dark : palette.button }
+ GradientStop { position: 1.0; color: dark }
+ }
+
+ TapHandler {
+ id: tapHandler
+ gesturePolicy: TapHandler.ReleaseWithinBounds
+ onTapped: button.clicked()
+ }
+
+ Text {
+ id: buttonLabel
+ text: "Click Me"
+ color: palette.buttonText
+ anchors.centerIn: parent
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml b/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml
new file mode 100644
index 0000000000..a6b1673d4c
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Item {
+ width: 120; height: 80
+
+ component Button : Rectangle {
+ id: button
+ signal clicked
+ property alias text: buttonLabel.text
+
+ width: 80
+ height: 40
+ radius: 3
+ property color dark: Qt.darker(palette.button, 1.3)
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: tapHandler.pressed ? dark : palette.button }
+ GradientStop { position: 1.0; color: dark }
+ }
+
+ SequentialAnimation on border.width {
+ id: tapFlash
+ running: false
+ loops: 3
+ PropertyAction { value: 2 }
+ PauseAnimation { duration: 100 }
+ PropertyAction { value: 0 }
+ PauseAnimation { duration: 100 }
+ }
+
+ //![1]
+ TapHandler {
+ id: tapHandler
+ gesturePolicy: TapHandler.ReleaseWithinBounds
+ onTapped: tapFlash.start()
+ }
+ //![1]
+
+ Text {
+ id: buttonLabel
+ text: "Click Me"
+ color: palette.buttonText
+ anchors.centerIn: parent
+ }
+ }
+
+ Button { x: 10; y: 10 }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonWithinBounds.qml b/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonWithinBounds.qml
new file mode 100644
index 0000000000..f2e968599c
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/tapHandlerButtonWithinBounds.qml
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Item {
+ width: 120; height: 80
+
+ component Button : Rectangle {
+ id: button
+ signal clicked
+ property alias text: buttonLabel.text
+
+ width: 80
+ height: 40
+ radius: 3
+ property color dark: Qt.darker(palette.button, 1.3)
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: tapHandler.pressed ? dark : palette.button }
+ GradientStop { position: 1.0; color: dark }
+ }
+
+ SequentialAnimation on border.width {
+ id: tapFlash
+ running: false
+ loops: 3
+ PropertyAction { value: 2 }
+ PauseAnimation { duration: 100 }
+ PropertyAction { value: 0 }
+ PauseAnimation { duration: 100 }
+ }
+
+ //![1]
+ TapHandler {
+ id: tapHandler
+ gesturePolicy: TapHandler.WithinBounds
+ onTapped: tapFlash.start()
+ }
+ //![1]
+
+ Text {
+ id: buttonLabel
+ text: "Click Me"
+ color: palette.buttonText
+ anchors.centerIn: parent
+ }
+ }
+
+ Button { x: 10; y: 10 }
+}
+//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/tapHandlerOnTapped.qml b/src/quick/doc/snippets/pointerHandlers/tapHandlerOnTapped.qml
index deff59d034..2c682df584 100644
--- a/src/quick/doc/snippets/pointerHandlers/tapHandlerOnTapped.qml
+++ b/src/quick/doc/snippets/pointerHandlers/tapHandlerOnTapped.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2019 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -48,7 +48,7 @@
**
****************************************************************************/
//![0]
-import QtQuick 2.12
+import QtQuick
Rectangle {
width: 100
@@ -56,8 +56,8 @@ Rectangle {
TapHandler {
acceptedButtons: Qt.LeftButton | Qt.RightButton
- onTapped: (eventPoint)=> console.log("tapped", eventPoint.event.device.name,
- "button", eventPoint.event.button,
+ onTapped: (eventPoint, button)=> console.log("tapped", eventPoint.device.name,
+ "button", button,
"@", eventPoint.scenePosition)
}
}
diff --git a/src/quick/doc/snippets/pointerHandlers/tapHandlerOverlappingButtons.qml b/src/quick/doc/snippets/pointerHandlers/tapHandlerOverlappingButtons.qml
new file mode 100644
index 0000000000..993330a5ec
--- /dev/null
+++ b/src/quick/doc/snippets/pointerHandlers/tapHandlerOverlappingButtons.qml
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick
+
+Item {
+ width: 120; height: 80
+
+ component Button : Rectangle {
+ signal clicked
+ property alias text: buttonLabel.text
+
+ width: 80
+ height: 40
+ radius: 3
+ property color dark: Qt.darker(palette.button, 1.3)
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: tapHandler.pressed ? dark : palette.button }
+ GradientStop { position: 1.0; color: dark }
+ }
+
+ SequentialAnimation on border.width {
+ id: tapFlash
+ running: false
+ loops: 3
+ PropertyAction { value: 2 }
+ PauseAnimation { duration: 100 }
+ PropertyAction { value: 0 }
+ PauseAnimation { duration: 100 }
+ }
+
+ //![1]
+ TapHandler {
+ id: tapHandler
+ gesturePolicy: TapHandler.DragThreshold // the default
+ onTapped: tapFlash.start()
+ }
+ //![1]
+
+ Text {
+ id: buttonLabel
+ text: "Click Me"
+ color: palette.buttonText
+ anchors.centerIn: parent
+ }
+ }
+
+ Button { x: 10; y: 10 }
+ Button { x: 30; y: 30 }
+}
+//![0]
diff --git a/src/quick/doc/src/advtutorial.qdoc b/src/quick/doc/src/advtutorial.qdoc
index 63c70067fd..66d0d125da 100644
--- a/src/quick/doc/src/advtutorial.qdoc
+++ b/src/quick/doc/src/advtutorial.qdoc
@@ -298,9 +298,6 @@ until the next chapter - where your application becomes alive!
Now we're going to do two things to liven up the game: animate the blocks and add a High Score system.
-We've also cleaned up the directory structure for our application files. We now have a lot of files, so all the
-JavaScript and QML files outside of \c samegame.qml have been moved into a new sub-directory named "content".
-
In anticipation of the new block animations, \c Block.qml file is now renamed to \c BoomBlock.qml.
\section3 Animating Block Movement
@@ -311,7 +308,7 @@ In \c BoomBlock.qml, we apply a \l SpringAnimation behavior to the \c x and \c y
block will follow and animate its movement in a spring-like fashion towards the specified position (whose
values will be set by \c samegame.js).Here is the code added to \c BoomBlock.qml:
-\snippet tutorials/samegame/samegame4/content/BoomBlock.qml 1
+\snippet tutorials/samegame/samegame4/BoomBlock.qml 1
The \c spring and \c damping values can be changed to modify the spring-like effect of the animation.
@@ -328,7 +325,7 @@ animate the opacity value so that it gradually fades in and out, instead of abru
visible and invisible. To do this, we'll apply a \l Behavior on the \c opacity property of the \c Image
type in \c BoomBlock.qml:
-\snippet tutorials/samegame/samegame4/content/BoomBlock.qml 2
+\snippet tutorials/samegame/samegame4/BoomBlock.qml 2
Note the \c{opacity: 0} which means the block is transparent when it is first created. We could set the opacity
in \c samegame.js when we create and destroy the blocks,
@@ -354,14 +351,14 @@ To fade out, we set \c dying to true instead of setting opacity to 0 when a bloc
Finally, we'll add a cool-looking particle effect to the blocks when they are destroyed. To do this, we first add a \l ParticleSystem in
\c BoomBlock.qml, like so:
-\snippet tutorials/samegame/samegame4/content/BoomBlock.qml 3
+\snippet tutorials/samegame/samegame4/BoomBlock.qml 3
To fully understand this you should read \l {Using the Qt Quick Particle System}, but it's important to note that \c emitRate is set
to zero so that particles are not emitted normally.
Also, we extend the \c dying State, which creates a burst of particles by calling the \c burst() method on the particles type. The code for the states now look
like this:
-\snippet tutorials/samegame/samegame4/content/BoomBlock.qml 4
+\snippet tutorials/samegame/samegame4/BoomBlock.qml 4
Now the game is beautifully animated, with subtle (or not-so-subtle) animations added for all of the
player's actions. The end result is shown below, with a different set of images to demonstrate basic theming:
@@ -378,20 +375,20 @@ To do this, we will show a dialog when the game is over to request the player's
This requires a few changes to \c Dialog.qml. In addition to a \c Text type, it now has a
\c TextInput child item for receiving keyboard text input:
-\snippet tutorials/samegame/samegame4/content/Dialog.qml 0
+\snippet tutorials/samegame/samegame4/Dialog.qml 0
\dots 4
-\snippet tutorials/samegame/samegame4/content/Dialog.qml 2
+\snippet tutorials/samegame/samegame4/Dialog.qml 2
\dots 4
-\snippet tutorials/samegame/samegame4/content/Dialog.qml 3
+\snippet tutorials/samegame/samegame4/Dialog.qml 3
We'll also add a \c showWithInput() function. The text input will only be visible if this function
is called instead of \c show(). When the dialog is closed, it emits a \c closed() signal, and
other types can retrieve the text entered by the user through an \c inputText property:
-\snippet tutorials/samegame/samegame4/content/Dialog.qml 0
-\snippet tutorials/samegame/samegame4/content/Dialog.qml 1
+\snippet tutorials/samegame/samegame4/Dialog.qml 0
+\snippet tutorials/samegame/samegame4/Dialog.qml 1
\dots 4
-\snippet tutorials/samegame/samegame4/content/Dialog.qml 3
+\snippet tutorials/samegame/samegame4/Dialog.qml 3
Now the dialog can be used in \c samegame.qml:
@@ -401,9 +398,9 @@ When the dialog emits the \c closed signal, we call the new \c saveHighScore() f
The \c nameInputDialog is activated in the \c victoryCheck() function in \c samegame.js:
-\snippet tutorials/samegame/samegame4/content/samegame.js 3
+\snippet tutorials/samegame/samegame4/samegame.js 3
\dots 4
-\snippet tutorials/samegame/samegame4/content/samegame.js 4
+\snippet tutorials/samegame/samegame4/samegame.js 4
\section3 Storing High Scores Offline
@@ -411,7 +408,7 @@ Now we need to implement the functionality to actually save the High Scores tabl
Here is the \c saveHighScore() function in \c samegame.js:
-\snippet tutorials/samegame/samegame4/content/samegame.js 2
+\snippet tutorials/samegame/samegame4/samegame.js 2
First we call \c sendHighScore() (explained in the section below) if it is possible to send the high scores to an online database.
@@ -430,7 +427,7 @@ If the player entered their name we can send the data to the web service us
If the player enters a name, we send the data to the service using this code in \c samegame.js:
-\snippet tutorials/samegame/samegame4/content/samegame.js 1
+\snippet tutorials/samegame/samegame4/samegame.js 1
The \l XMLHttpRequest in this code is the same as the \c XMLHttpRequest() as you'll find in standard browser JavaScript, and can be used in the same way to dynamically get XML
or QML from the web service to display the high scores. We don't worry about the response in this case - we just post the high
diff --git a/src/quick/doc/src/cmake-macros.qdoc b/src/quick/doc/src/cmake-macros.qdoc
deleted file mode 100644
index b643a9e4e4..0000000000
--- a/src/quick/doc/src/cmake-macros.qdoc
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** 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 Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
-\page qtqml-cmake-qt5-import-qml-plugins.html
-\ingroup cmake-macros-qtqml
-
-\title qt5_import_qml_plugins
-
-\brief Scans \c{.qml} files and imports required QML static plugins
-
-\section1 Overview
-
-\badcode
-find_package(Qt5QmlImportScanner REQUIRED)
-qt5_import_qml_plugins(<TARGET>)
-\endcode
-
-\section1 Description
-
-Runs \c{qmlimportscanner} at configure time to find the static QML plugins
-used and links them to the given target.
-
-\note When used with a non-static Qt build, this function does nothing.
-
-This CMake command was introduced in Qt 5.14.
-
-\section1 Example
-
-\snippet cmake-macros/examples.cmake qt5_import_qml_plugins
-
-*/
diff --git a/src/quick/doc/src/concepts/effects/particles.qdoc b/src/quick/doc/src/concepts/effects/particles.qdoc
index abe721c5ab..aa89450439 100644
--- a/src/quick/doc/src/concepts/effects/particles.qdoc
+++ b/src/quick/doc/src/concepts/effects/particles.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
- \qmlmodule QtQuick.Particles 2.\QtMinorVersion
+ \qmlmodule QtQuick.Particles
\title Qt Quick Particles QML Types
\ingroup qmlmodules
\brief Provides QML types for particle effects
diff --git a/src/quick/doc/src/concepts/input/focus.qdoc b/src/quick/doc/src/concepts/input/focus.qdoc
index 9862489f42..6dd5a4b4bc 100644
--- a/src/quick/doc/src/concepts/input/focus.qdoc
+++ b/src/quick/doc/src/concepts/input/focus.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -76,8 +76,9 @@ type whose text is determined by whether or not it has active focus.
An \l Item requests focus by setting the \c focus property to \c true.
For very simple cases simply setting the \c focus property is sometimes
-sufficient. If we run the following example with \l{qtquick-qmlscene.html}
-{qmlscene}, we see that the \c {keyHandler} type has active focus and
+sufficient. If we run the following example with the
+\l {Prototyping with the QML Runtime Tool}{qml tool},
+we see that the \c {keyHandler} type has active focus and
pressing the \c A, \c B, or \c C keys modifies the text appropriately.
\snippet qml/focus/basicwidget.qml focus true
@@ -196,7 +197,7 @@ property. As the \l ListView is a focus scope, this doesn't affect the
rest of the application. However, if the \l ListView itself has
active focus this causes the delegate itself to receive active focus.
In this example, the root type of the delegate is also a focus scope,
-which in turn gives active focus to the \c {Text} type that actually performs
+which in turn gives active focus to the \l {TextInput} type that actually performs
the work of handling the \c {Return} key.
All of the QML view classes, such as \l PathView and \l GridView, behave
diff --git a/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc b/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc
index 2ac9860e6f..7a05583a82 100644
--- a/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc
+++ b/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -30,20 +30,23 @@
\title Qt Quick Input Handlers
\brief A module with a set of QML elements that handle events from input devices in a user interface.
- Qt Quick Input Handlers are a set of QML types used to handle events from
- keyboard, touch, mouse, and stylus devices in a UI. In contrast to event-handling
- items, such as \l MouseArea and \l Flickable, input handlers are explicitly non-visual,
- require less memory and are intended to be used in greater numbers: one
- handler instance per aspect of interaction. Each input handler instance
- handles certain events on behalf of its \c parent Item. Thus the visual and
+ Qt Quick Input Handlers are a set of QML types used to handle
+ \l {QInputEvent}{events} from keyboard, touch, mouse, and stylus
+ \l {QInputDevice}{devices} in a UI. In contrast to event-handling
+ items, such as \l MouseArea and \l Flickable, input handlers are explicitly
+ non-visual, require less memory and are intended to be used in greater
+ numbers: one handler instance per aspect of interaction. Each input handler
+ instance handles certain events on behalf of its
+ \l {QQuickPointerHandler::parent()}{parent} Item. Thus the visual and
behavioral concerns are better separated, and the behavior is built up by
finer-grained composition.
- In Qt 5.10, these handlers were introduced in a separate Qt.labs.handlers module.
- Now they are included with Qt Quick since 5.12. The pre-existing
- \l Keys attached property is similar in concept, so we refer to the
- pointing-device-oriented handlers plus \c Keys together as the set of Input Handlers.
- We expect to offer more attached-property use cases in future versions of Qt.
+ The \l {Pointer Handlers Example} demonstrates some use cases for these.
+
+ The pre-existing \l Keys attached property is similar in concept, so we
+ refer to the pointing-device-oriented handlers plus \c Keys together as the
+ set of Input Handlers. We expect to offer more attached-property use cases
+ in future versions of Qt.
\section1 Input Handlers
@@ -60,7 +63,45 @@
\li Each Item can have unlimited Handlers
\endlist
- \omit TODO actual overview with snippets and stuff \endomit
+ \section1 Handlers Manipulating Items
+
+ Some Handlers add interactivity simply by being declared inside an Item:
+
+ \snippet pointerHandlers/dragHandler.qml 0
+
+ \section1 Handler Properties and Signals
+
+ All Handlers have properties that can be used in bindings, and signals that
+ can be handled to react to input:
+
+ \snippet pointerHandlers/hoverTapKeyButton.qml 0
+
+ \section1 Pointer Grab
+
+ An important concept with Pointer Handlers is the type of grabs that they
+ perform. The only kind of grab an Item can take is the exclusive grab: for
+ example if you call \l QPointerEvent::setExclusiveGrabber(), the following
+ mouse moves and mouse release event will be sent only to that object. (As a
+ workaround to this exclusivity, see \l QQuickItem::setFiltersChildMouseEvents()
+ and \l QQuickItem::childMouseEventFilter().) However Pointer Handlers have
+ an additional mechanism available: the
+ \l {QPointerEvent::addPassiveGrabber()} {passive grab}. Mouse and touch
+ \l {QEventPoint::state()}{press} events are delivered by visiting all the
+ Items in top-down Z order: first each Item's child Handlers, and then the
+ \l {QQuickItem::event()}{Item} itself. At the time a press event is
+ delivered, a Handler can take either a passive or an exclusive grab
+ depending on its needs. If it takes a passive grab, it is guaranteed to
+ receive the updates and the release, even if other Items or Handlers in the
+ scene take any kind of grab, passive or exclusve. Some Handlers (such as
+ PointHandler) can work only with passive grabs; others require exclusive
+ grabs; and others can "lurk" with passive grabs until they detect that a
+ gesture is being performed, and then make the transition from passive to
+ exclusive grab. TapHandler's grabbing behavior is
+ \l {TapHandler::gesturePolicy}{configurable}.
+
+ When a grab transition is requested, \l PointerHandler::grabPermissions,
+ \l QQuickItem::keepMouseGrab() and \l QQuickItem::keepTouchGrab() control
+ whether the transition will be allowed.
\section1 Related Information
diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc
index 369c2f4444..429bd6e854 100644
--- a/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc
+++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc
@@ -103,9 +103,16 @@
\l{Layout::minimumWidth}{minimum}, \l{Layout::preferredWidth}{preferred},
and \l{Layout::maximumWidth}{maximum} sizes of all items where \l{Layout::fillWidth}{Layout.fillWidth} or
\l{Layout::fillHeight}{Layout.fillHeight} is set to \c true.
+
+ The \l{Layout::preferredWidth}{preferred} width and height is the \e actual width and
+ height of an item if the layout is not bound to a specific size itself. If the layout is set
+ to a specific size, it will distribute additional space based on the ratio of preferred sizes
+ of its items (taking minimum and maximum sizes into account).
+
For instance, the following will produce a layout with two rectangles lying side-by-side that
stretches horizontally. The azure rectangle can be resized from 50x150 to 300x150, and the plum
- rectangle can be resized from 100x100 to ∞x100.
+ rectangle can be resized from 100x100 to ∞x100. As long as the minimum and maximum width of each
+ item is not reached, the plum rectangle will have two times the width of the azure one.
\snippet qml/windowconstraints.qml rowlayout
diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc
index 9ea8759318..0df832cca9 100644
--- a/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc
+++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
- \qmlmodule QtQuick.Layouts 1.\QtMinorVersion
+ \qmlmodule QtQuick.Layouts
\title Qt Quick Layouts QML Types
\ingroup qmlmodules
\brief Provides QML types for arranging QML items in a user interface.
diff --git a/src/quick/doc/src/concepts/positioning/righttoleft.qdoc b/src/quick/doc/src/concepts/positioning/righttoleft.qdoc
index 1f3602cde1..574dfcb58f 100644
--- a/src/quick/doc/src/concepts/positioning/righttoleft.qdoc
+++ b/src/quick/doc/src/concepts/positioning/righttoleft.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -175,17 +175,11 @@ You can test that the layout direction works as expected by running your Qt Quic
the compiled translation file:
\code
-qmlscene myapp.qml -translation myapp.qm
+qml myapp.qml -translation myapp.qm
\endcode
-You can test your application in right-to-left layout direction simply by executing qmlscene with a
-command-line parameter "-reverse":
-
-\code
-qmlscene myapp.qml -reverse
-\endcode
-
-The layout direction can also be set from C++ by calling the static function \l QGuiApplication::setLayoutDirection():
+You can test your application in right-to-left layout direction by calling the
+static function \l QGuiApplication::setLayoutDirection():
\code
QGuiApplication app(argc, argv);
diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
index faec8df7e4..b0a66cfb5f 100644
--- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
@@ -194,6 +194,7 @@ throttling, use the \c basic render loop instead by setting \c
{QSG_RENDER_LOOP=basic} in the environment.
\section2 Threaded Render Loop ('threaded')
+\target threaded_render_loop
On many configurations, the scene graph rendering will happen on a
dedicated render thread. This is done to increase parallelism of
diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
index 7f5e0fa760..9aa404a612 100644
--- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
+++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
@@ -52,11 +52,16 @@ options that align with the latest UI design trends. If these UI controls do not
satisfy your application's needs, only then it is recommended to create a
custom control.
+You can use the controls when you design UIs in Qt Design Studio. In addition,
+it provides timeline-based animations, visual effects, layouts, and a
+live-preview for prototyping applications.
\section2 Related Information
\list
\li \l{Qt Quick Controls}
+\li \l{Customizing Qt Quick Controls}
\li \l{Qt Quick}
+\li \l{Qt Design Studio Manual}
\endlist
\omit
@@ -87,66 +92,37 @@ of the target OS.
For example, consider the following project directory structure:
\badcode
-project
+MyModule
├── images
│ ├── image1.png
│ └── image2.png
-├── project.pro
-└── qml
- └── main.qml
+├── CMakeLists.txt
+└── main.qml
\endcode
-The following entry in \c project.pro ensures that the resources are built into
-the application binary, making them available when needed:
+You may represent this structure as a \l{qt_add_qml_module}{CMake QML Module} in the following way:
-\badcode
- RESOURCES += \
- qml/main.qml \
- images/image1.png \
- images/image2.png
-\endcode
-
-A more convenient approach is to use the
-\l {files(pattern[, recursive=false])}{wildcard syntax} to select several files
-at once:
-
-\badcode
- RESOURCES += \
- $$files(qml/*.qml) \
- $$files(images/*.png)
-\endcode
-
-This approach is convenient for applications that depend on a limited number
-of resources. However, whenever a new file is added to \c RESOURCES using this
-approach, it causes \e all of the other files in \c RESOURCES to be recompiled
-as well. This can be inefficient, especially for large sets of files.
-In this case, a better approach is to separate each type of resource into its
-own \l [Qt Core] {Resource Collection Files}{.qrc} file. For example, the snippet
-above could be changed to the following:
-
-\badcode
- qml.files = $$files(*.qml)
- qml.prefix = /qml
- RESOURCES += qml
-
- images.files = $$files(*.png)
- images.prefix = /images
- RESOURCES += images
+\code
+qt_add_qml_module(my_module
+ URI MyModule
+ VERSION 1.0
+ QML_FILES
+ main.qml
+ RESOURCES
+ images/image1.png
+ images/image2.png
+ # ...
+)
\endcode
-Now, whenever a QML file is changed, only the QML files have to be recompiled.
-
-Sometimes it can be necessary to have more control over the path for a
-specific file managed by the resource system. For example, if we wanted to give
-\c image2.png an alias, we would need to switch to an explicit \c .qrc file.
-\l {Creating Resource Files} explains how to do this in detail.
+All QML files listed under \c {QML_FILES} will automatically get compiled \l {Ahead-of-Time Compilation}{ahead of time}.
\section2 Related Information
\list
\li \l{The Qt Resource System}
\endlist
-\section1 Separate UI from Logic
+\section1 Separate UI from Business Logic
One of the key goals that most application developers want to achieve is to
create a maintainable application. One of the ways to achieve this goal is
@@ -162,8 +138,8 @@ reasons why an application's UI should be written in QML:
\li JavaScript can easily be used in QML to respond to events.
\endlist
-Being a strongly typed language, C++ is best suited for an application's logic.
-Typically, such code performs tasks such as complex calculations
+Being a strongly typed language, C++ is best suited for an application's
+business logic. Typically, such code performs tasks such as complex calculations
or data processing, which are faster in C++ than QML.
Qt offers various approaches to integrate QML and C++ code in an application.
@@ -174,12 +150,6 @@ sufficient.
The following snippet demonstrates examples of models written in QML:
\qml
- model: ListModel {
- ListElement { name: "Item 1" }
- ListElement { name: "Item 2" }
- ListElement { name: "Item 3" }
- }
-
model: [ "Item 1", "Item 2", "Item 3" ]
model: 10
@@ -188,135 +158,21 @@ The following snippet demonstrates examples of models written in QML:
Use \l {QAbstractItemModel Subclass}{C++} for dynamic data sets that are large
or frequently modified.
-\section2 Interacting with QML from C++
-
-Although Qt enables you to manipulate QML from C++, it is not recommended to do
-so. To explain why, let's take a look at a simplified example.
-
-\section3 Pulling References from QML
-
-Suppose we were writing the UI for a settings page:
-
-\qml
- import QtQuick
- import QtQuick.Controls
-
- Page {
- Button {
- text: qsTr("Restore default settings")
- }
- }
-\endqml
-
-We want the button to do something in C++ when it is clicked. We know objects
-in QML can emit change signals just like they can in C++, so we give the button
-an \l [QML]{QtObject::}{objectName} so that we can find it from C++:
-
-\qml
- Button {
- objectName: "restoreDefaultsButton"
- text: qsTr("Restore default settings")
- }
-\endqml
-
-Then, in C++, we find that object and connect to its change signal:
-
-\code
- #include <QGuiApplication>
- #include <QQmlApplicationEngine>
- #include <QSettings>
-
- class Backend : public QObject
- {
- Q_OBJECT
-
- public:
- Backend() {}
-
- public slots:
- void restoreDefaults() {
- settings.setValue("loadLastProject", QVariant(false));
- }
-
- private:
- QSettings settings;
- };
-
- int main(int argc, char *argv[])
- {
- QGuiApplication app(argc, argv);
-
- QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
- if (engine.rootObjects().isEmpty())
- return -1;
-
- Backend backend;
-
- QObject *rootObject = engine.rootObjects().first();
- QObject *restoreDefaultsButton = rootObject->findChild<QObject*>("restoreDefaultsButton");
- QObject::connect(restoreDefaultsButton, SIGNAL(clicked()),
- &backend, SLOT(restoreDefaults()));
-
- return app.exec();
- }
-
- #include "main.moc"
-\endcode
-
-With this approach, references to objects are "pulled" from QML.
-The problem with this is that the C++ logic layer depends on the QML
-presentation layer. If we were to refactor the QML in such a way that the
-\c objectName changes, or some other change breaks the ability for the C++
-to find the QML object, our workflow becomes much more complicated and tedious.
-
-\section3 Pushing References to QML
+\section2 Exposing Data from C++ to QML
Refactoring QML is a lot easier than refactoring C++, so in order to make
maintenance pain-free, we should strive to keep C++ types unaware of QML as
much as possible. This can be achieved by "pushing" references to C++ types
-into QML:
-
-\code
- int main(int argc, char *argv[])
- {
- QGuiApplication app(argc, argv);
-
- Backend backend;
+into QML.
- QQmlApplicationEngine engine;
- engine.rootContext()->setContextProperty("backend", &backend);
- engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
- if (engine.rootObjects().isEmpty())
- return -1;
-
- return app.exec();
- }
-\endcode
-
-The QML then calls the C++ slot directly:
-
-\qml
- import QtQuick
- import QtQuick.Controls
-
- Page {
- Button {
- text: qsTr("Restore default settings")
- onClicked: backend.restoreDefaults()
- }
- }
-\endqml
+This can be done by using \l {Required properties}{required properties} and
+setting them via \l QQmlApplicationEngine::setInitialProperties. It is also
+possible to create one or multiple \l {QML_SINGLETON}{singletons} which will
+return all the data the C++ side wants to provide to QML.
With this approach, the C++ remains unchanged in the event that the QML needs
to be refactored in the future.
-In the example above, we set a context property on the root context to expose
-the C++ object to QML. This means that the property is available to every
-component loaded by the engine. Context properties are useful for objects that
-must be available as soon as the QML is loaded and cannot be instantiated in
-QML.
-
For a quick guide to choosing the correct approach to expose C++ types to QML,
see \l {Choosing the Correct Integration Method Between C++ and QML}.
@@ -326,6 +182,22 @@ see \l {Choosing the Correct Integration Method Between C++ and QML}.
\li \l{Qt Quick Controls - Chat Tutorial}{Chat application tutorial}
\endlist
+\section1 Using Qt Design Studio
+
+Qt Design Studio uses UI files that have the filename extension \e {.ui.qml}
+to separate the visual parts of the UI from the UI logic you implement in
+\e {.qml} files. You should edit UI files only in the \uicontrol {2D} view in
+Qt Design Studio. If you use some other tool to add code that Qt Design Studio
+does not support, it displays error messages. Fix the errors to enable visual
+editing of the UI files again. Typically, you should move the unsupported code
+to a \e {.qml} file.
+
+\section2 Related Information
+
+\list
+ \li \l{Qt Design Studio: UI Files}
+\endlist
+
\section1 Using Qt Quick Layouts
Qt offers Qt Quick Layouts to arrange Qt Quick items visually in a layout.
diff --git a/src/quick/doc/src/guidelines/qtquick-tool-qmllint.qdoc b/src/quick/doc/src/guidelines/qtquick-tool-qmllint.qdoc
index ad40e850d5..0f21e1cae3 100644
--- a/src/quick/doc/src/guidelines/qtquick-tool-qmllint.qdoc
+++ b/src/quick/doc/src/guidelines/qtquick-tool-qmllint.qdoc
@@ -65,6 +65,47 @@ add them via the \c{-I} flag.
To get an overview and explanation of all available command line options, run \c{qmllint --help}.
+\section2 Disabling warnings inline
+
+You may at any point disable warnings temporarily in a file using \c{// qmllint
+disable}.
+
+You can do this at the end of a line when a single line produces warnings:
+
+\code
+Item {
+ property string foo
+ Item {
+ property string bar: foo // qmllint disable unqualified
+ }
+}
+\endcode
+
+Alternatively you can disable comments for a block of lines by putting the
+comment in a line only containing \c{// qmllint disable}, ending the block with
+\c{// qmllint enable}:
+
+\code
+Item {
+ property string foo
+ Item {
+ // qmllint disable unqualified
+ property string bar: foo
+ property string bar2: foo
+ // qmllint enable unqualified
+ }
+}
+\endcode
+
+qmllint interprets all single line comments starting with \c {qmllint} as
+directives. Thus you may not start a comment that way unless you wish to enable
+or disable warnings.
+
+\note As done in the examples above it is preferable to explicitly specify the
+warning or a list of warnings you want to disable instead of disabling all
+warnings. This can be done by simply listing warning categories after \c{qmllint disable} (the names are
+the same as the options listed in \c{--help}).
+
\section2 Settings
In addition to passing command-line options, you can also
@@ -94,5 +135,5 @@ level.
This can be used to more easily integrate qmllint in your pre-commit hooks or
CI testing.
-\sa \l{Type Description Files}{qmltypes}
+\sa {Type Description Files}
*/
diff --git a/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc b/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc
index 34dc4c2c22..96d2163eda 100644
--- a/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc
+++ b/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc
@@ -35,83 +35,64 @@ especially for Qt Quick developers. The following sections provide a brief
introduction to those tools and utilities, and provide links to further
information about them.
-\section1 Qt Quick Designer
+\section1 Qt Creator
+
+The \l{Qt Creator Manual}{Qt Creator} IDE is the key tool that enhances the overall developer
+experience of working with Qt and Qt Quick. Its editing, formatting, profiling and
+debugging features for Qt Quick make working with Qt Quick easier.
+
+\section1 Qt Design Studio
-The Qt Quick Designer enables designing Qt Quick-based UIs using simple
+\l{Qt Design Studio} enables designing Qt Quick-based UIs using simple
drag-n-drop gestures that most designers are familiar with. It offers UI
elements from the Qt Quick and Qt Quick Controls modules, as well as
integration for custom UI elements.
-The following is a list of example applications that use UIs created by
-the Qt Quick Designer:
-
-\list
- \li \l{Qt Quick Controls - Contact List}
- \li \l{Qt Quick Controls - Flat Style}
-\endlist
-
-\section2 QML Debugger and Profiler
+\section1 QML Debugger
-Being a declarative language, a piece of QML code provides minimal details
-about the entities defined. In such a scenario, the QML debugger is a very
-useful utility that enables:
+The \l{QML debugger} is a very useful utility that enables:
\list
\li debugging JavaScript functions,
\li executing JavaScript expressions,
\li and inspecting QML properties.
\endlist
-Besides this, a QML profiler enables you to get necessary diagnostic information,
+The QML debugger is part of both \l{Qt Creator} and \l{Qt Design Studio}.
+
+\section1 QML Profiler
+
+The \l{QML profiler} enables you to get necessary diagnostic information,
allowing you to analyze the application code for performance issues. For
example, too much JavaScript in each frame, long-running C++ functions, and
so on.
-\section2 Related Information
-\list
- \li \l{QML Debugger}
- \li \l{QML Profiler}
-\endlist
+The profiler is part of both \l{Qt Creator} and \l{Qt Design Studio}.
-\section1 QmlLive, GammaRay, and Squish
+\section1 QmlLive
-QmlLive is a 3rd party tool that offers a QML runtime capable of rendering
+\l{QmlLive} is a 3rd party tool that offers a QML runtime capable of rendering
changes to the code in realtime. It avoids the need to rebuild the
application after every code change and install it on the target device.
You can also extend it to build a custom runtime that suits your needs.
-GammaRay is a useful utility that provides diagnostic information
+\section1 GammaRay
+
+\l{GammaRay} is a useful utility that provides diagnostic information
about your application. It is similar to the QML Profiler described in the
earlier section, but offers a lot more. For example, the number of items or
QObjects created, function calls made, time taken by each function call,
property value introspection at runtime, and so on. Such information is very
handy, especially while debugging QML applications.
-Squish is a well-known testing tool that automates UI testing by recording
+\section1 Squish
+
+\l{Squish} is a well-known testing tool that automates UI testing by recording
your actions or running scripts. Once the tests are setup, UI tests are a lot
easier to run.
-\section2 Related Information
-\list
- \li \l{QmlLive}
- \li \l{GammaRay}
- \li \l{Squish}
-\endlist
-
-\section1 Qt Creator
-
-The Qt Creator IDE is the key tool that enhances the overall developer experience of
-working with Qt Quick. Its auto-completion and debugging features make working
-with Qt Quick easier. Besides this, most of the tools and utilities
-mentioned in the earlier sections are integrated into it, with the possibility of
-integrating 3rd party tools such as QmlLive and GammaRay.
-
-\section2 Related Information
-\list
-\li \l{Qt Creator Manual}
-\endlist
-
\section1 qmllint
-\e qmllint is a tool shipped with Qt, that verifies the syntatic validity of QML files.
+
+\l{qmllint} is a tool shipped with Qt, that verifies the syntatic validity of QML files.
It also warns about some QML anti-patterns. If you want to disable a specific
warning type, you can find the appropriate flag for doing so by passing \c{--help} on the command line.
@@ -130,7 +111,4 @@ by specifying the \c{-n} flag.
By default, qmlformat writes the formatted version of the file to stdout.
If you wish to have your file updated in-place specify the \c{-i} flag.
-
-\section2 Related Information
-\sa {QML Coding Conventions}
*/
diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc
index b86725f4ad..e47a137330 100644
--- a/src/quick/doc/src/qmltypereference.qdoc
+++ b/src/quick/doc/src/qmltypereference.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
-\qmlmodule QtQuick 2.\QtMinorVersion
+\qmlmodule QtQuick
\title Qt Quick QML Types
\ingroup qmlmodules
\brief Provides graphical QML types.
diff --git a/src/quick/doc/src/qt6-changes.qdoc b/src/quick/doc/src/qt6-changes.qdoc
index 58a7ebb3e1..defacd95a1 100644
--- a/src/quick/doc/src/qt6-changes.qdoc
+++ b/src/quick/doc/src/qt6-changes.qdoc
@@ -138,7 +138,7 @@
OpenGL. It will not be functional when using another graphics API, such as
Vulkan or Metal. Applications relying on QQuickWidget should force the usage
of OpenGL by calling
- \c{QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi)} in their
+ \c{QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL)} in their
main() function.
\section2 Changes to QQuick* APIs
@@ -206,7 +206,7 @@
not be functional when using another graphics API, such as Vulkan or Metal.
Applications relying on QQuickFramebufferObject should force the usage of
OpenGL by calling
- \c{QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi)} in their
+ \c{QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL)} in their
main() function.
\li QQuickRenderControl has a slightly changed API: grab() is now
diff --git a/src/quick/doc/src/qtquick.qdoc b/src/quick/doc/src/qtquick.qdoc
index 46dfa5651f..a8665a3641 100644
--- a/src/quick/doc/src/qtquick.qdoc
+++ b/src/quick/doc/src/qtquick.qdoc
@@ -148,6 +148,7 @@ Additional Qt Quick information:
\li \l{Qt Quick Test QML Types}{Tests} - contains types for writing unit test for a QML application
\endlist
\li \l{Qt Quick Examples and Tutorials}
+\li \l{Qt Quick Tools and Utilities}
\li \l{Best Practices for QML and Qt Quick}{Qt Quick Guidelines}
\endlist
diff --git a/src/quick/doc/src/tutorial.qdoc b/src/quick/doc/src/tutorial.qdoc
index e84753d895..040685fae2 100644
--- a/src/quick/doc/src/tutorial.qdoc
+++ b/src/quick/doc/src/tutorial.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -100,11 +100,13 @@ The \c font.pointSize and \c font.bold properties are related to fonts and use t
\section2 Viewing the Example
-To view what you have created, run the \l{Prototyping with qmlscene}{qmlscene} tool (located in the \c bin directory) with your filename as the first argument.
-For example, to run the provided completed Tutorial 1 example from the install location, you would type:
+To view what you have created, run the
+\l{Prototyping with the QML Runtime Tool}{qml tool} (located in the
+\c bin directory) with your filename as the first argument. For example, to run
+the provided completed Tutorial 1 example from the install location, you would type:
\code
-qmlscene tutorials/helloworld/tutorial1.qml
+qml tutorials/helloworld/tutorial1.qml
\endcode
*/
diff --git a/src/quick/handlers/qquickdragaxis.cpp b/src/quick/handlers/qquickdragaxis.cpp
index 88470c8a7d..806b5d705a 100644
--- a/src/quick/handlers/qquickdragaxis.cpp
+++ b/src/quick/handlers/qquickdragaxis.cpp
@@ -76,3 +76,5 @@ void QQuickDragAxis::setEnabled(bool enabled)
}
QT_END_NAMESPACE
+
+#include "moc_qquickdragaxis_p.cpp"
diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp
index 233516c3a3..8c9464ee55 100644
--- a/src/quick/handlers/qquickdraghandler.cpp
+++ b/src/quick/handlers/qquickdraghandler.cpp
@@ -88,7 +88,7 @@ Q_LOGGING_CATEGORY(lcDragHandler, "qt.quick.handler.drag")
At this time, drag-and-drop is not yet supported.
- \sa Drag, MouseArea
+ \sa Drag, MouseArea, {Pointer Handlers Example}
*/
QQuickDragHandler::QQuickDragHandler(QQuickItem *parent)
@@ -131,13 +131,13 @@ void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QPointingDe
This property holds the snap mode.
- The snap mode configures snapping of the \l target item's center to the event point.
+ The snap mode configures snapping of the \l target item's center to the \l eventPoint.
Possible values:
\value DragHandler.SnapNever Never snap
- \value DragHandler.SnapAuto The \l target snaps if the event point was pressed outside of the \l target
- item \e and the \l target is a descendant of \l parentItem (default)
- \value DragHandler.SnapWhenPressedOutsideTarget The \l target snaps if the event point was pressed outside of the \l target
+ \value DragHandler.SnapAuto The \l target snaps if the \l eventPoint was pressed outside of the \l target
+ item \e and the \l target is a descendant of \l {PointerHandler::}{parent} item (default)
+ \value DragHandler.SnapWhenPressedOutsideTarget The \l target snaps if the \l eventPoint was pressed outside of the \l target
\value DragHandler.SnapAlways Always snap
*/
QQuickDragHandler::SnapMode QQuickDragHandler::snapMode() const
@@ -371,7 +371,7 @@ void QQuickDragHandler::setActiveTranslation(const QVector2D &trans)
/*!
\readonly
\qmlproperty QVector2D QtQuick::DragHandler::translation
- \deprecated Use activeTranslation
+ \deprecated [6.2] Use activeTranslation
*/
/*!
@@ -395,3 +395,5 @@ void QQuickDragHandler::setActiveTranslation(const QVector2D &trans)
*/
QT_END_NAMESPACE
+
+#include "moc_qquickdraghandler_p.cpp"
diff --git a/src/quick/handlers/qquickhandlerpoint.cpp b/src/quick/handlers/qquickhandlerpoint.cpp
index c3149983c0..52513f8273 100644
--- a/src/quick/handlers/qquickhandlerpoint.cpp
+++ b/src/quick/handlers/qquickhandlerpoint.cpp
@@ -49,7 +49,7 @@ Q_DECLARE_LOGGING_CATEGORY(lcTouchTarget)
\inqmlmodule QtQuick
\brief An event point.
- A QML representation of a QEventPoint.
+ A handler-owned QML representation of a QEventPoint.
It's possible to make bindings to properties of a handler's current
\l {SinglePointHandler::point}{point} or
@@ -58,12 +58,12 @@ Q_DECLARE_LOGGING_CATEGORY(lcTouchTarget)
\snippet pointerHandlers/dragHandlerNullTarget.qml 0
The point is kept up-to-date when the DragHandler is actively responding to
- an EventPoint; but after the point is released, or when the current point is
+ an \l eventPoint; but after the point is released, or when the current point is
being handled by a different handler, \c position.x and \c position.y are 0.
- \note This is practically identical to QtQuick::EventPoint; however an
- EventPoint is a long-lived QObject which is invalidated between gestures
- and reused for subsequent event deliveries. Continuous bindings to its
+ \note This is practically identical to \l eventPoint; however an eventPoint
+ is a short-lived copy of a long-lived Q_GADGET which is invalidated between
+ gestures and reused for subsequent event deliveries. Continuous bindings to its
properties are not possible, and an individual handler cannot rely on it
outside the period when that point is part of an active gesture which that
handler is handling. HandlerPoint is a Q_GADGET that the handler owns.
@@ -186,7 +186,7 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
During a touch gesture, from the time that the first finger is pressed
until the last finger is released, each touchpoint will have a unique ID
number. Likewise, if input from multiple devices occurs (for example
- simultaneous mouse and touch presses), all the current event points from
+ simultaneous mouse and touch presses), all the current \l{eventPoint}{eventPoints} from
all the devices will have unique IDs.
\note Do not assume that id numbers start at zero or that they are
@@ -223,7 +223,7 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
\qmlproperty QPointF QtQuick::HandlerPoint::position
\brief The position within the \c parent Item
- This is the position of the event point relative to the bounds of
+ This is the position of the \l eventPoint relative to the bounds of
the \l {PointerHandler::parent} {parent}.
*/
@@ -232,7 +232,7 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
\qmlproperty QPointF QtQuick::HandlerPoint::scenePosition
\brief The position within the scene
- This is the position of the event point relative to the bounds of the Qt
+ This is the position of the \l eventPoint relative to the bounds of the Qt
Quick scene (typically the whole window).
*/
@@ -290,7 +290,7 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
This is a velocity vector pointing in the direction of movement, in logical
pixels per second. It has x and y components, at least one of which will be
nonzero when this point is in motion. It holds the average recent velocity:
- how fast and in which direction the event point has been moving recently.
+ how fast and in which direction the \l eventPoint has been moving recently.
\sa QtQuick::TouchPoint::velocity, QEventPoint::velocity
*/
@@ -345,4 +345,13 @@ void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
\sa QtQuick::TouchPoint::ellipseDiameters, QEventPoint::ellipseDiameters
*/
+/*!
+ \readonly
+ \qmlproperty PointerDevice QtQuick::handlerPoint::device
+
+ This property holds the device that the point (and its event) came from.
+*/
+
QT_END_NAMESPACE
+
+#include "moc_qquickhandlerpoint_p.cpp"
diff --git a/src/quick/handlers/qquickhoverhandler.cpp b/src/quick/handlers/qquickhoverhandler.cpp
index e953a5b311..c5067881a6 100644
--- a/src/quick/handlers/qquickhoverhandler.cpp
+++ b/src/quick/handlers/qquickhoverhandler.cpp
@@ -67,7 +67,7 @@ Q_LOGGING_CATEGORY(lcHoverHandler, "qt.quick.handler.hover")
The \l cursorShape property allows changing the cursor whenever
\l hovered changes to \c true.
- \sa MouseArea, PointHandler
+ \sa MouseArea, PointHandler, {Pointer Handlers Example}
*/
QQuickHoverHandler::QQuickHoverHandler(QQuickItem *parent)
@@ -101,10 +101,9 @@ bool QQuickHoverHandler::event(QEvent *event)
void QQuickHoverHandler::componentComplete()
{
- if (auto par = parentItem()) {
- par->setAcceptHoverEvents(true);
+ QQuickSinglePointHandler::componentComplete();
+ if (auto par = parentItem())
QQuickItemPrivate::get(par)->setHasHoverInChild(true);
- }
}
bool QQuickHoverHandler::wantsPointerEvent(QPointerEvent *event)
@@ -164,6 +163,98 @@ void QQuickHoverHandler::setHovered(bool hovered)
}
/*!
+ \internal
+ \qmlproperty flags QtQuick::HoverHandler::acceptedButtons
+
+ This property is not used in HoverHandler.
+*/
+
+/*!
+ \qmlproperty flags QtQuick::HoverHandler::acceptedDevices
+
+ The types of pointing devices that can activate the pointer handler.
+
+ By default, this property is set to
+ \l{QInputDevice::DeviceType}{PointerDevice.AllDevices}.
+ If you set it to an OR combination of device types, it will ignore pointer
+ events from the non-matching devices.
+
+ For example, an item could be made to respond to mouse hover in one way,
+ and stylus hover in another way, with two handlers:
+
+ \snippet pointerHandlers/hoverMouseOrStylus.qml 0
+
+ The available device types are as follows:
+
+ \value PointerDevice.Mouse A mouse.
+ \value PointerDevice.TouchScreen A touchscreen.
+ \value PointerDevice.TouchPad A touchpad or trackpad.
+ \value PointerDevice.Stylus A stylus on a graphics tablet.
+ \value PointerDevice.Airbrush An airbrush on a graphics tablet.
+ \value PointerDevice.Puck A digitizer with crosshairs, on a graphics tablet.
+ \value PointerDevice.AllDevices Any type of pointing device.
+
+ \sa QInputDevice::DeviceType
+*/
+
+/*!
+ \qmlproperty flags QtQuick::HoverHandler::acceptedPointerTypes
+
+ The types of pointing instruments (generic, stylus, eraser, and so on)
+ that can activate the pointer handler.
+
+ By default, this property is set to
+ \l {QPointingDevice::PointerType} {PointerDevice.AllPointerTypes}.
+ If you set it to an OR combination of device types, it will ignore events
+ from non-matching events.
+
+ For example, you could provide feedback by changing the cursor depending on
+ whether a stylus or eraser is hovering over a graphics tablet:
+
+ \snippet pointerHandlers/hoverStylusOrEraser.qml 0
+
+ The available pointer types are as follows:
+
+ \value PointerDevice.Generic A mouse or a device that emulates a mouse.
+ \value PointerDevice.Finger A finger on a touchscreen (hover detection is unlikely).
+ \value PointerDevice.Pen A stylus on a graphics tablet.
+ \value PointerDevice.Eraser An eraser on a graphics tablet.
+ \value PointerDevice.Cursor A digitizer with crosshairs, on a graphics tablet.
+ \value PointerDevice.AllPointerTypes Any type of pointing device.
+
+ \sa QPointingDevice::PointerType
+*/
+
+/*!
+ \qmlproperty flags QtQuick::HoverHandler::acceptedModifiers
+
+ If this property is set, a hover event is handled only if the given keyboard
+ modifiers are pressed. The event is ignored without the modifiers.
+
+ This property is set to \c Qt.KeyboardModifierMask by default, resulting
+ in handling hover events regardless of any 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:
+
+ \snippet pointerHandlers/hoverModifiers.qml 0
+
+ The available modifiers are as follows:
+
+ \value Qt.NoModifier No modifier key is allowed.
+ \value Qt.ShiftModifier A Shift key on the keyboard must be pressed.
+ \value Qt.ControlModifier A Ctrl key on the keyboard must be pressed.
+ \value Qt.AltModifier An Alt key on the keyboard must be pressed.
+ \value Qt.MetaModifier A Meta key on the keyboard must be pressed.
+ \value Qt.KeypadModifier A keypad button must be pressed.
+ \value Qt.GroupSwitchModifier A Mode_switch key on the keyboard must be pressed.
+ X11 only (unless activated on Windows by a command line argument).
+ \value Qt.KeyboardModifierMask The handler ignores modifier keys.
+
+ \sa Qt::KeyboardModifier
+*/
+
+/*!
\since 5.15
\qmlproperty Qt::CursorShape QtQuick::HoverHandler::cursorShape
This property holds the cursor shape that will appear whenever
@@ -211,4 +302,13 @@ void QQuickHoverHandler::setHovered(bool hovered)
\sa Qt::CursorShape, QQuickItem::cursor()
*/
+/*!
+ \internal
+ \qmlproperty flags HoverHandler::dragThreshold
+
+ This property is not used in HoverHandler.
+*/
+
QT_END_NAMESPACE
+
+#include "moc_qquickhoverhandler_p.cpp"
diff --git a/src/quick/handlers/qquickmultipointhandler.cpp b/src/quick/handlers/qquickmultipointhandler.cpp
index 8c13c84914..70ceeb47fc 100644
--- a/src/quick/handlers/qquickmultipointhandler.cpp
+++ b/src/quick/handlers/qquickmultipointhandler.cpp
@@ -185,7 +185,7 @@ QVector<QEventPoint> QQuickMultiPointHandler::eligiblePoints(QPointerEvent *even
// In other cases however, check whether it would be OK to steal the grab if the handler chooses to do that.
bool stealingAllowed = event->isBeginEvent() || event->isEndEvent();
for (int i = 0; i < event->pointCount(); ++i) {
- auto &p = QMutableEventPoint::from(event->point(i));
+ auto &p = event->point(i);
if (QQuickDeliveryAgentPrivate::isMouseEvent(event)) {
if (static_cast<QMouseEvent *>(event)->buttons() == Qt::NoButton)
continue;
@@ -398,7 +398,7 @@ bool QQuickMultiPointHandler::grabPoints(QPointerEvent *event, const QVector<QEv
}
}
if (allowed) {
- for (auto point : points)
+ for (const auto &point : qAsConst(points))
setExclusiveGrab(event, point);
}
return allowed;
@@ -444,3 +444,5 @@ QMetaProperty &QQuickMultiPointHandlerPrivate::yMetaProperty() const
}
QT_END_NAMESPACE
+
+#include "moc_qquickmultipointhandler_p.cpp"
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp
index bdacc6f46d..cb45509de0 100644
--- a/src/quick/handlers/qquickpinchhandler.cpp
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -92,7 +92,7 @@ Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch")
but if it's a disallowed number, it does not scale or rotate
its \l target, and the \l active property remains \c false.
- \sa PinchArea, QPointerEvent::pointCount(), QNativeGestureEvent::fingerCount()
+ \sa PinchArea, QPointerEvent::pointCount(), QNativeGestureEvent::fingerCount(), {Pointer Handlers Example}
*/
QQuickPinchHandler::QQuickPinchHandler(QQuickItem *parent)
@@ -217,6 +217,7 @@ bool QQuickPinchHandler::wantsPointerEvent(QPointerEvent *event)
*/
/*!
+ \readonly
\qmlproperty bool QtQuick::PinchHandler::active
This property is \c true when all the constraints (epecially
@@ -238,8 +239,8 @@ void QQuickPinchHandler::onActiveChanged()
m_startRotation = t->rotation();
m_startPos = t->position();
} else {
- m_startScale = 1;
- m_startRotation = 0;
+ m_startScale = m_accumulatedScale;
+ m_startRotation = 0; // TODO m_accumulatedRotation (QTBUG-94168)
}
qCDebug(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation;
} else {
@@ -271,6 +272,7 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
return;
case Qt::ZoomNativeGesture:
m_activeScale *= 1 + gesture->value();
+ m_activeScale = qBound(m_minimumScale, m_activeScale, m_maximumScale);
break;
case Qt::RotateNativeGesture:
m_activeRotation += gesture->value();
@@ -460,6 +462,13 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
}
/*!
+ \internal
+ \qmlproperty flags QtQuick::PinchHandler::acceptedButtons
+
+ This property is not used in PinchHandler.
+*/
+
+/*!
\readonly
\qmlproperty QtQuick::HandlerPoint QtQuick::PinchHandler::centroid
@@ -506,6 +515,11 @@ void QQuickPinchHandler::handlePointerEventImpl(QPointerEvent *event)
The translation of the gesture \l centroid. It is \c (0, 0) when the
gesture begins.
+
+ \note On some touchpads, such as on a \macos trackpad, native gestures do
+ not generate any translation values, and this property stays at \c (0, 0).
*/
QT_END_NAMESPACE
+
+#include "moc_qquickpinchhandler_p.cpp"
diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp
index 27f9c3fc36..9fe214824f 100644
--- a/src/quick/handlers/qquickpointerdevicehandler.cpp
+++ b/src/quick/handlers/qquickpointerdevicehandler.cpp
@@ -175,7 +175,7 @@ void QQuickPointerDeviceHandler::setAcceptedDevices(QPointingDevice::DeviceTypes
By default, this property is set to
\l {QPointingDevice::PointerType} {PointerDevice.AllPointerTypes}.
If you set it to an OR combination of device types, it will ignore events
- from non-matching events.
+ from non-matching \l {PointerDevice}{devices}.
For example, a control could be made to respond to mouse, touch, and stylus clicks
in some way, but delete itself if tapped with an eraser tool on a graphics tablet,
@@ -315,3 +315,5 @@ bool QQuickPointerDeviceHandler::wantsPointerEvent(QPointerEvent *event)
}
QT_END_NAMESPACE
+
+#include "moc_qquickpointerdevicehandler_p.cpp"
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp
index 597ce17c35..ec3c972af3 100644
--- a/src/quick/handlers/qquickpointerhandler.cpp
+++ b/src/quick/handlers/qquickpointerhandler.cpp
@@ -92,7 +92,7 @@ QQuickPointerHandler::~QQuickPointerHandler()
\qmlproperty real PointerHandler::margin
The margin beyond the bounds of the \l {PointerHandler::parent}{parent}
- item within which an event point can activate this handler. For example, on
+ item within which an \l eventPoint can activate this handler. For example, on
a PinchHandler where the \l {PointerHandler::target}{target} is also the
\c parent, it's useful to set this to a distance at least half the width
of a typical user's finger, so that if the \c parent has been scaled down
@@ -125,7 +125,7 @@ void QQuickPointerHandler::setMargin(qreal pointDistanceThreshold)
\qmlproperty int PointerHandler::dragThreshold
\since 5.15
- The distance in pixels that the user must drag an event point in order to
+ The distance in pixels that the user must drag an \l eventPoint in order to
have it treated as a drag gesture.
The default value depends on the platform and screen resolution.
@@ -217,9 +217,11 @@ void QQuickPointerHandler::setCursorShape(Qt::CursorShape shape)
return;
d->cursorShape = shape;
d->cursorSet = true;
- QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(parentItem());
- itemPriv->hasCursorHandler = true;
- itemPriv->setHasCursorInChild(true);
+ if (auto *parent = parentItem()) {
+ QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(parent);
+ itemPriv->hasCursorHandler = true;
+ itemPriv->setHasCursorInChild(true);
+ }
emit cursorShapeChanged();
}
@@ -230,9 +232,11 @@ void QQuickPointerHandler::resetCursorShape()
return;
d->cursorShape = Qt::ArrowCursor;
d->cursorSet = false;
- QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(parentItem());
- itemPriv->hasCursorHandler = false;
- itemPriv->setHasCursorInChild(itemPriv->hasCursor);
+ if (auto *parent = parentItem()) {
+ QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(parent);
+ itemPriv->hasCursorHandler = false;
+ itemPriv->setHasCursorInChild(itemPriv->hasCursor);
+ }
emit cursorShapeChanged();
}
@@ -248,8 +252,8 @@ bool QQuickPointerHandler::isCursorShapeExplicitlySet() const
The \a grabber (subject) will be the Input Handler whose state is changing,
or null if the state change regards an Item.
The \a transition (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 \a point (object) is the \l eventPoint that was grabbed or ungrabbed.
+ QQuickDeliveryAgent calls this function.
The Input Handler must react in whatever way is appropriate, and must
emit the relevant signals (for the benefit of QML code).
A subclass is allowed to override this virtual function, but must always
@@ -432,6 +436,8 @@ bool QQuickPointerHandler::approveGrabTransition(QPointerEvent *event, const QEv
This handler can take the exclusive grab from another handler of the same class.
\value PointerHandler.CanTakeOverFromHandlersOfDifferentType
This handler can take the exclusive grab from any kind of handler.
+ \value PointerHandler.CanTakeOverFromItems
+ This handler can take the exclusive grab from any type of Item.
\value PointerHandler.CanTakeOverFromAnything
This handler can take the exclusive grab from any type of Item or Handler.
\value PointerHandler.ApprovesTakeOverByHandlersOfSameType
@@ -472,6 +478,14 @@ void QQuickPointerHandler::classBegin()
void QQuickPointerHandler::componentComplete()
{
+ Q_D(const QQuickPointerHandler);
+ if (d->cursorSet) {
+ if (auto *parent = parentItem()) {
+ QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(parent);
+ itemPriv->hasCursorHandler = true;
+ itemPriv->setHasCursorInChild(true);
+ }
+ }
}
QPointerEvent *QQuickPointerHandler::currentEvent()
@@ -646,10 +660,12 @@ bool QQuickPointerHandler::event(QEvent *e)
void QQuickPointerHandler::handlePointerEvent(QPointerEvent *event)
{
+ Q_D(QQuickPointerHandler);
bool wants = wantsPointerEvent(event);
qCDebug(lcPointerHandlerDispatch) << metaObject()->className() << objectName()
<< "on" << parent()->metaObject()->className() << parent()->objectName()
<< (wants ? "WANTS" : "DECLINES") << event;
+ d->currentEvent = event;
if (wants) {
handlePointerEventImpl(event);
} else {
@@ -665,6 +681,7 @@ void QQuickPointerHandler::handlePointerEvent(QPointerEvent *event)
}
}
}
+ d->currentEvent = nullptr;
QQuickPointerHandlerPrivate::deviceDeliveryTargets(event->device()).append(this);
}
@@ -690,9 +707,9 @@ bool QQuickPointerHandler::wantsEventPoint(const QPointerEvent *event, const QEv
\qmlproperty bool QtQuick::PointerHandler::active
This holds true whenever this Input Handler has taken sole responsibility
- for handing one or more EventPoints, by successfully taking an exclusive
- grab of those points. This means that it is keeping its properties
- up-to-date according to the movements of those Event Points and actively
+ for handing one or more \l {eventPoint}{eventPoints}, by successfully taking an
+ exclusive grab of those points. This means that it is keeping its properties
+ up-to-date according to the movements of those eventPoints and actively
manipulating its \l target (if any).
*/
void QQuickPointerHandler::setActive(bool active)
@@ -706,10 +723,8 @@ void QQuickPointerHandler::setActive(bool active)
}
}
-void QQuickPointerHandler::handlePointerEventImpl(QPointerEvent *event)
+void QQuickPointerHandler::handlePointerEventImpl(QPointerEvent *)
{
- Q_D(QQuickPointerHandler);
- d->currentEvent = event;
}
/*!
@@ -718,7 +733,7 @@ void QQuickPointerHandler::handlePointerEventImpl(QPointerEvent *event)
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
+ pointer event is relevant if at least one of its \l {eventPoint}{eventPoints} occurs within
the Item's interior. Initially \l [QML] {target} {target()} is the same, but it
can be reassigned.
@@ -729,7 +744,7 @@ void QQuickPointerHandler::handlePointerEventImpl(QPointerEvent *event)
*/
/*!
- \qmlsignal QtQuick::PointerHandler::grabChanged(GrabTransition transition, EventPoint point)
+ \qmlsignal QtQuick::PointerHandler::grabChanged(PointerDevice::GrabTransition transition, eventPoint point)
This signal is emitted when the grab has changed in some way which is
relevant to this handler.
@@ -793,3 +808,5 @@ QVector<QObject *> &QQuickPointerHandlerPrivate::deviceDeliveryTargets(const QIn
}
QT_END_NAMESPACE
+
+#include "moc_qquickpointerhandler_p.cpp"
diff --git a/src/quick/handlers/qquickpointhandler.cpp b/src/quick/handlers/qquickpointhandler.cpp
index 3ce95db51a..94d1f2f75c 100644
--- a/src/quick/handlers/qquickpointhandler.cpp
+++ b/src/quick/handlers/qquickpointhandler.cpp
@@ -102,7 +102,8 @@ QT_BEGIN_NAMESPACE
PointHandler will not automatically manipulate the \c target item in any way.
You need to use bindings to make it react to the \l point.
- \note On macOS, PointHandler does not react to the trackpad by default.
+ \note On macOS, PointHandler does not react to multiple fingers on the
+ trackpad by default, although it does react to a pressed point (mouse position).
That is because macOS can provide either native gesture recognition, or raw
touchpoints, but not both. We prefer to use the native gesture event in
PinchHandler, so we do not want to disable it by enabling touch. However
@@ -111,7 +112,7 @@ QT_BEGIN_NAMESPACE
want to react to all the touchpoints but do not require the smooth
native-gesture experience.
- \sa MultiPointTouchArea
+ \sa MultiPointTouchArea, HoverHandler, {Pointer Handlers Example}
*/
QQuickPointHandler::QQuickPointHandler(QQuickItem *parent)
@@ -164,4 +165,127 @@ QVector2D QQuickPointHandler::translation() const
return QVector2D(point().position() - point().pressPosition());
}
+/*!
+ \qmlproperty flags PointHandler::acceptedButtons
+
+ The mouse buttons that can activate this PointHandler.
+
+ By default, this property is set to \l {QtQuick::MouseEvent::button} {Qt.LeftButton}.
+ It can be set to an OR combination of mouse buttons, and will ignore events
+ in which other buttons are pressed or held.
+
+ \snippet pointerHandlers/pointHandlerAcceptedButtons.qml 0
+
+ \note On a touchscreen, there are no buttons, so this property does not
+ prevent PointHandler from reacting to touchpoints.
+*/
+
+/*!
+ \qmlproperty flags PointHandler::acceptedDevices
+
+ The types of pointing devices that can activate this PointHandler.
+
+ By default, this property is set to
+ \l{QInputDevice::DeviceType}{PointerDevice.AllDevices}.
+ If you set it to an OR combination of device types, it will ignore events
+ from non-matching \l {PointerDevice}{devices}:
+
+ \snippet pointerHandlers/pointHandler.qml 1
+*/
+
+/*!
+ \qmlproperty flags PointHandler::acceptedPointerTypes
+
+ The types of pointing instruments (finger, stylus, eraser, etc.)
+ that can activate this PointHandler.
+
+ By default, this property is set to
+ \l {QPointingDevice::PointerType} {PointerDevice.AllPointerTypes}.
+ If you set it to an OR combination of device types, it will ignore events
+ from non-matching \l {PointerDevice}{devices}:
+
+ \snippet pointerHandlers/pointHandlerCanvasDrawing.qml 0
+
+ The \l {Pointer Handlers Example} includes a more complex example for
+ drawing on a Canvas with a graphics tablet.
+*/
+
+/*!
+ \qmlproperty flags PointHandler::acceptedModifiers
+
+ If this property is set, PointHandler requires the given keyboard modifiers
+ to be pressed in order to react to \l {PointerEvent}{PointerEvents}, and
+ otherwise ignores them.
+
+ If this property is set to \c Qt.KeyboardModifierMask (the default value),
+ then PointHandler ignores the modifier keys.
+
+ For example, an \l [QML] Item could have two handlers, one of which is
+ enabled only if the required keyboard modifier is pressed:
+
+ \snippet pointerHandlers/pointHandlerAcceptedModifiers.qml 0
+
+ If you set \c acceptedModifiers to an OR combination of modifier keys,
+ it means \e all of those modifiers must be pressed to activate the handler.
+
+ The available modifiers are as follows:
+
+ \value NoModifier No modifier key is allowed.
+ \value ShiftModifier A Shift key on the keyboard must be pressed.
+ \value ControlModifier A Ctrl key on the keyboard must be pressed.
+ \value AltModifier An Alt key on the keyboard must be pressed.
+ \value MetaModifier A Meta key on the keyboard must be pressed.
+ \value KeypadModifier A keypad button must be pressed.
+ \value GroupSwitchModifier X11 only (unless activated on Windows by a command line argument).
+ A Mode_switch key on the keyboard must be pressed.
+ \value KeyboardModifierMask The handler does not care which modifiers are pressed.
+
+ \sa Qt::KeyboardModifier
+*/
+
+/*!
+ \readonly
+ \qmlproperty bool PointHandler::active
+
+ This holds \c true whenever the constraints are satisfied and this
+ PointHandler is reacting. This means that it is keeping its properties
+ up-to-date according to the movements of the \l {eventPoint}{eventPoints}
+ that satisfy the constraints.
+*/
+
+/*!
+ \internal
+ \qmlproperty flags PointHandler::dragThreshold
+
+ This property is not used in PointHandler.
+*/
+
+/*!
+ \qmlproperty real PointHandler::margin
+
+ The margin beyond the bounds of the \l {PointerHandler::parent}{parent}
+ item within which an \l eventPoint can activate this handler.
+
+ The default value is \c 0.
+
+ \snippet pointerHandlers/pointHandlerMargin.qml 0
+*/
+
+/*!
+ \qmlproperty real PointHandler::target
+
+ A property that can conveniently hold an Item to be manipulated or to show
+ feedback. Unlike other \l {Qt Quick Input Handlers}{Pointer Handlers},
+ PointHandler does not do anything with the \c target on its own: you
+ usually need to create reactive bindings to properties such as
+ \l SinglePointHandler::point and \l PointHandler::active. If you declare
+ an Item instance here, you need to explicitly set its \l {Item::}{parent},
+ because PointHandler is not an Item.
+
+ By default, it is the same as the \l {PointerHandler::}{parent}, the Item
+ within which the handler is declared.
+*/
+
QT_END_NAMESPACE
+
+#include "moc_qquickpointhandler_p.cpp"
diff --git a/src/quick/handlers/qquicksinglepointhandler.cpp b/src/quick/handlers/qquicksinglepointhandler.cpp
index aa650654eb..22b475637a 100644
--- a/src/quick/handlers/qquicksinglepointhandler.cpp
+++ b/src/quick/handlers/qquicksinglepointhandler.cpp
@@ -209,7 +209,7 @@ QQuickHandlerPoint QQuickSinglePointHandler::point() const
\readonly
\qmlproperty HandlerPoint QtQuick::SinglePointHandler::point
- The event point currently being handled. When no point is currently being
+ The \l eventPoint currently being handled. When no point is currently being
handled, this object is reset to default values (all coordinates are 0).
*/
@@ -226,3 +226,5 @@ void QQuickSinglePointHandlerPrivate::reset()
}
QT_END_NAMESPACE
+
+#include "moc_qquicksinglepointhandler_p.cpp"
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
index d16c9e23cb..3abb366676 100644
--- a/src/quick/handlers/qquicktaphandler.cpp
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -77,12 +77,14 @@ int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1);
button in order to cancel the click. For this use case, set the
\l gesturePolicy to \c TapHandler.ReleaseWithinBounds.
+ \snippet pointerHandlers/tapHandlerButton.qml 0
+
For multi-tap gestures (double-tap, triple-tap etc.), the distance moved
must not exceed QStyleHints::mouseDoubleClickDistance() with mouse and
QStyleHints::touchDoubleTapDistance() with touch, and the time between
taps must not exceed QStyleHints::mouseDoubleClickInterval().
- \sa MouseArea
+ \sa MouseArea, {Pointer Handlers Example}
*/
QQuickTapHandler::QQuickTapHandler(QQuickItem *parent)
@@ -180,7 +182,7 @@ void QQuickTapHandler::handleEventPoint(QPointerEvent *event, QEventPoint &point
/*!
\qmlproperty real QtQuick::TapHandler::longPressThreshold
- The time in seconds that an event point must be pressed in order to
+ The time in seconds that an \l eventPoint 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
@@ -225,31 +227,79 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event)
If the spatial constraint is violated, \l pressed transitions immediately
from true to false, regardless of the time held.
- \value TapHandler.DragThreshold
- (the default value) 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
- input handlers (for example \l DragHandler) or event-handling Items
- (for example QtQuick Controls), because in this case TapHandler
- will not take the exclusive grab, but merely a passive grab.
-
- \value TapHandler.WithinBounds
- If the event point leaves the bounds of the \c parent Item, the tap
- gesture is canceled. The TapHandler will take the exclusive grab on
- press, but will release the grab as soon as the boundary constraint
- is no longer satisfied.
-
- \value TapHandler.ReleaseWithinBounds
- 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
- \c parent Item, a tap gesture is not recognized. This corresponds to
- typical behavior for button widgets: 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 take the exclusive grab on press and retain
- it until release in order to detect this gesture.
+ The \c gesturePolicy also affects grab behavior as described below.
+
+ \table
+ \header
+ \li Constant
+ \li Description
+ \row
+ \li \c TapHandler.DragThreshold
+ \image pointerHandlers/tapHandlerOverlappingButtons.webp
+ Grab on press: \e passive
+ \li (the default value) The \l eventPoint 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 device or finger is still pressed. This policy
+ can be useful whenever TapHandler needs to cooperate with other
+ input handlers (for example \l DragHandler) or event-handling Items
+ (for example \l {Qt Quick Controls}), because in this case TapHandler
+ will not take the exclusive grab, but merely a
+ \l {QPointerEvent::addPassiveGrabber()}{passive grab}.
+ That is, \c DragThreshold is especially useful to \e augment
+ existing behavior: it reacts to tap/click/long-press even when
+ another item or handler is already reacting, perhaps even in a
+ different layer of the UI. The following snippet shows one
+ TapHandler as used in one component; but if we stack up two
+ instances of the component, you will see the handlers in both of them
+ react simultaneously when a press occurs over both of them, because
+ the passive grab does not stop event propagation:
+ \quotefromfile pointerHandlers/tapHandlerOverlappingButtons.qml
+ \skipto Item
+ \printuntil component Button
+ \skipto TapHandler
+ \printuntil }
+ \skipuntil Text {
+ \skipuntil }
+ \printuntil Button
+ \printuntil Button
+ \printuntil }
+
+ \row
+ \li \c TapHandler.WithinBounds
+ \image pointerHandlers/tapHandlerButtonWithinBounds.webp
+ Grab on press: \e exclusive
+ \li If the \l eventPoint leaves the bounds of the \c parent Item, the tap
+ gesture is canceled. The TapHandler will take the
+ \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on
+ press, but will release the grab as soon as the boundary constraint
+ is no longer satisfied.
+ \snippet pointerHandlers/tapHandlerButtonWithinBounds.qml 1
+
+ \row
+ \li \c TapHandler.ReleaseWithinBounds
+ \image pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp
+ Grab on press: \e exclusive
+ \li At the time of release (the mouse button is released or the finger
+ is lifted), if the \l eventPoint is outside the bounds of the
+ \c parent Item, a tap gesture is not recognized. This corresponds to
+ typical behavior for button widgets: 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 take the
+ \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on press
+ and retain it until release in order to detect this gesture.
+ \snippet pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml 1
+ \endtable
+
+ The \l {Pointer Handlers Example} demonstrates some use cases for these.
+
+ \note If you find that TapHandler is reacting in cases that conflict with
+ some other behavior, the first thing you should try is to think about which
+ \c gesturePolicy is appropriate. If you cannot fix it by changing \c gesturePolicy,
+ some cases are better served by adjusting \l {PointerHandler::}{grabPermissions},
+ either in this handler, or in another handler that should \e prevent TapHandler
+ from reacting.
*/
void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gesturePolicy)
{
@@ -266,7 +316,7 @@ void QQuickTapHandler::setGesturePolicy(QQuickTapHandler::GesturePolicy gestureP
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
+ \l gesturePolicy. When the \l eventPoint is released or the policy is
violated, \e pressed will change to false.
*/
void QQuickTapHandler::setPressed(bool press, bool cancel, QPointerEvent *event, QEventPoint &point)
@@ -303,12 +353,13 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QPointerEvent *event,
else
m_tapCount = 1;
qCDebug(lcTapHandler) << objectName() << "tapped" << m_tapCount << "times";
- emit tapped(point);
+ auto button = event->isSinglePointEvent() ? static_cast<QSinglePointEvent *>(event)->button() : Qt::NoButton;
+ emit tapped(point, button);
emit tapCountChanged();
if (m_tapCount == 1)
- emit singleTapped(point);
+ emit singleTapped(point, button);
else if (m_tapCount == 2)
- emit doubleTapped(point);
+ emit doubleTapped(point, button);
m_lastTapTimestamp = ts;
m_lastTapPos = point.scenePosition();
} else {
@@ -392,7 +443,7 @@ void QQuickTapHandler::updateTimeHeld()
*/
/*!
- \qmlsignal QtQuick::TapHandler::tapped(EventPoint eventPoint)
+ \qmlsignal QtQuick::TapHandler::tapped(EventPoint eventPoint, Qt::MouseButton button)
This signal is emitted each time the \c parent Item is tapped.
@@ -400,24 +451,28 @@ void QQuickTapHandler::updateTimeHeld()
period less than \l longPressThreshold, while any movement does not exceed
the drag threshold, then the \c tapped signal will be emitted at the time
of release. The \a eventPoint signal parameter contains information
- from the release event about the point that was tapped:
+ from the release event about the point that was tapped, and \a button
+ is the \l {Qt::MouseButton}{mouse button} that was clicked, or \c NoButton
+ on a touchscreen.
\snippet pointerHandlers/tapHandlerOnTapped.qml 0
*/
/*!
- \qmlsignal QtQuick::TapHandler::singleTapped(EventPoint eventPoint)
+ \qmlsignal QtQuick::TapHandler::singleTapped(EventPoint eventPoint, Qt::MouseButton button)
\since 5.11
This signal is emitted when the \c parent Item is tapped once.
After an amount of time greater than QStyleHints::mouseDoubleClickInterval,
it can be tapped again; but if the time until the next tap is less,
\l tapCount will increase. The \a eventPoint signal parameter contains
- information from the release event about the point that was tapped.
+ information from the release event about the point that was tapped, and
+ \a button is the \l {Qt::MouseButton}{mouse button} that was clicked, or
+ \c NoButton on a touchscreen.
*/
/*!
- \qmlsignal QtQuick::TapHandler::doubleTapped(EventPoint eventPoint)
+ \qmlsignal QtQuick::TapHandler::doubleTapped(EventPoint eventPoint, Qt::MouseButton button)
\since 5.11
This signal is emitted when the \c parent Item is tapped twice within a
@@ -426,7 +481,9 @@ void QQuickTapHandler::updateTimeHeld()
QStyleHints::touchDoubleTapDistance()). This signal always occurs after
\l singleTapped, \l tapped, and \l tapCountChanged. The \a eventPoint
signal parameter contains information from the release event about the
- point that was tapped.
+ point that was tapped, and \a button is the
+ \l {Qt::MouseButton}{mouse button} that was clicked, or \c NoButton
+ on a touchscreen.
*/
/*!
@@ -447,3 +504,5 @@ void QQuickTapHandler::updateTimeHeld()
from the previous \c tapCount.
*/
QT_END_NAMESPACE
+
+#include "moc_qquicktaphandler_p.cpp"
diff --git a/src/quick/handlers/qquicktaphandler_p.h b/src/quick/handlers/qquicktaphandler_p.h
index d75547eee2..2e0c8e211f 100644
--- a/src/quick/handlers/qquicktaphandler_p.h
+++ b/src/quick/handlers/qquicktaphandler_p.h
@@ -97,9 +97,10 @@ Q_SIGNALS:
void timeHeldChanged();
void longPressThresholdChanged();
void gesturePolicyChanged();
- void tapped(QEventPoint eventPoint);
- void singleTapped(QEventPoint eventPoint);
- void doubleTapped(QEventPoint eventPoint);
+ // the second argument (Qt::MouseButton) was added in 6.2: avoid name clashes with IDs by not naming it for now
+ void tapped(QEventPoint eventPoint, Qt::MouseButton /* button */);
+ void singleTapped(QEventPoint eventPoint, Qt::MouseButton /* button */);
+ void doubleTapped(QEventPoint eventPoint, Qt::MouseButton /* button */);
void longPressed();
protected:
diff --git a/src/quick/handlers/qquickwheelhandler.cpp b/src/quick/handlers/qquickwheelhandler.cpp
index 7045f10d8e..f1e40ba35c 100644
--- a/src/quick/handlers/qquickwheelhandler.cpp
+++ b/src/quick/handlers/qquickwheelhandler.cpp
@@ -81,7 +81,7 @@ Q_LOGGING_CATEGORY(lcWheelHandler, "qt.quick.handler.wheel")
WheelHandler handles only a rotating mouse wheel by default; this
can be changed by setting acceptedDevices.
- \sa MouseArea, Flickable
+ \sa MouseArea, Flickable, {Pointer Handlers Example}
*/
QQuickWheelHandler::QQuickWheelHandler(QQuickItem *parent)
@@ -91,7 +91,7 @@ QQuickWheelHandler::QQuickWheelHandler(QQuickItem *parent)
}
/*!
- \qmlproperty enum QtQuick::WheelHandler::orientation
+ \qmlproperty enumeration QtQuick::WheelHandler::orientation
Which wheel to react to. The default is \c Qt.Vertical.
@@ -502,7 +502,7 @@ void QQuickWheelHandler::timerEvent(QTimerEvent *event)
}
/*!
- \qmlsignal QtQuick::WheelHandler::wheel(PointerScrollEvent event)
+ \qmlsignal QtQuick::WheelHandler::wheel(WheelEvent event)
This signal is emitted every time this handler receives an \a event
of type \l QWheelEvent: that is, every time the wheel is moved or the
@@ -548,3 +548,5 @@ QMetaProperty &QQuickWheelHandlerPrivate::targetMetaProperty() const
*/
QT_END_NAMESPACE
+
+#include "moc_qquickwheelhandler_p.cpp"
diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp
index e13c0f6dae..89fafbe44d 100644
--- a/src/quick/items/context2d/qquickcanvasitem.cpp
+++ b/src/quick/items/context2d/qquickcanvasitem.cpp
@@ -288,7 +288,7 @@ QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate()
QPainter instead of the more expensive and likely less performing
JavaScript and Context2D approach.
- \sa Context2D QQuickPaintedItem
+ \sa Context2D, QQuickPaintedItem, {Pointer Handlers Example}
*/
QQuickCanvasItem::QQuickCanvasItem(QQuickItem *parent)
@@ -652,6 +652,10 @@ void QQuickCanvasItem::invalidateSceneGraph()
d->textureProvider = nullptr;
delete d->nodeTexture;
d->nodeTexture = nullptr;
+
+ // As we can expect(/hope) that the SG will be "good again", we can requestPaint ( which does 'markDirty(canvasWindow);' )
+ // Otherwise this Canvas will be "blank" when SG comes back
+ requestPaint();
}
void QQuickCanvasItem::schedulePolish()
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index 64b8b80f8b..32546c5299 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -2459,7 +2459,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_beginPath(const QV4::Funct
\image qml-item-canvas-bezierCurveTo.png
\sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-beziercurveto}{W3C 2d context standard for bezierCurveTo}
- \sa {http://www.openrise.com/lab/FlowerPower/}{The beautiful flower demo by using bezierCurveTo}
+ \sa {https://web.archive.org/web/20130505222636if_/http://www.openrise.com/lab/FlowerPower/}{The beautiful flower demo by using bezierCurveTo}
*/
QV4::ReturnedValue QQuickJSContext2DPrototype::method_bezierCurveTo(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
{
@@ -3884,16 +3884,16 @@ void QQuickContext2D::bezierCurveTo(qreal cp1x, qreal cp1y,
m_path.cubicTo(QPointF(cp1x, cp1y), QPointF(cp2x, cp2y), pt);
}
-void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radius)
+void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, qreal radius)
{
QPointF p0(m_path.currentPosition());
QPointF p1p0((p0.x() - p1.x()), (p0.y() - p1.y()));
QPointF p1p2((p2.x() - p1.x()), (p2.y() - p1.y()));
- float p1p0_length = std::sqrt(p1p0.x() * p1p0.x() + p1p0.y() * p1p0.y());
- float p1p2_length = std::sqrt(p1p2.x() * p1p2.x() + p1p2.y() * p1p2.y());
+ qreal p1p0_length = std::hypot(p1p0.x(), p1p0.y());
+ qreal p1p2_length = std::hypot(p1p2.x(), p1p2.y());
- double cos_phi = (p1p0.x() * p1p2.x() + p1p0.y() * p1p2.y()) / (p1p0_length * p1p2_length);
+ qreal cos_phi = QPointF::dotProduct(p1p0, p1p2) / (p1p0_length * p1p2_length);
// The points p0, p1, and p2 are on the same straight line (HTML5, 4.8.11.1.8)
// We could have used areCollinear() here, but since we're reusing
@@ -3903,16 +3903,16 @@ void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radiu
return;
}
- float tangent = radius / std::tan(std::acos(cos_phi) / 2);
- float factor_p1p0 = tangent / p1p0_length;
+ qreal tangent = radius / std::tan(std::acos(cos_phi) / 2);
+ qreal factor_p1p0 = tangent / p1p0_length;
QPointF t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y()));
QPointF orth_p1p0(p1p0.y(), -p1p0.x());
- float orth_p1p0_length = std::sqrt(orth_p1p0.x() * orth_p1p0.x() + orth_p1p0.y() * orth_p1p0.y());
- float factor_ra = radius / orth_p1p0_length;
+ qreal orth_p1p0_length = std::hypot(orth_p1p0.x(), orth_p1p0.y());
+ qreal factor_ra = radius / orth_p1p0_length;
// angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0
- double cos_alpha = (orth_p1p0.x() * p1p2.x() + orth_p1p0.y() * p1p2.y()) / (orth_p1p0_length * p1p2_length);
+ qreal cos_alpha = QPointF::dotProduct(orth_p1p0, p1p2) / (orth_p1p0_length * p1p2_length);
if (cos_alpha < 0.f)
orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
@@ -3920,20 +3920,15 @@ void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radiu
// calculate angles for addArc
orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
- float sa = std::acos(orth_p1p0.x() / orth_p1p0_length);
- if (orth_p1p0.y() < 0.f)
- sa = 2 * M_PI - sa;
+ qreal sa = std::atan2(orth_p1p0.y(), orth_p1p0.x());
// anticlockwise logic
bool anticlockwise = false;
- float factor_p1p2 = tangent / p1p2_length;
+ qreal factor_p1p2 = tangent / p1p2_length;
QPointF t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y()));
QPointF orth_p1p2((t_p1p2.x() - p.x()), (t_p1p2.y() - p.y()));
- float orth_p1p2_length = std::sqrt(orth_p1p2.x() * orth_p1p2.x() + orth_p1p2.y() * orth_p1p2.y());
- float ea = std::acos(orth_p1p2.x() / orth_p1p2_length);
- if (orth_p1p2.y() < 0)
- ea = 2 * M_PI - ea;
+ qreal ea = std::atan2(orth_p1p2.y(), orth_p1p2.x());
if ((sa > ea) && ((sa - ea) < M_PI))
anticlockwise = true;
if ((sa < ea) && ((ea - sa) > M_PI))
diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h
index 92ec1e6470..ea1354725f 100644
--- a/src/quick/items/context2d/qquickcontext2d_p.h
+++ b/src/quick/items/context2d/qquickcontext2d_p.h
@@ -240,7 +240,7 @@ public:
void arc(qreal x, qreal y, qreal radius,
qreal startAngle, qreal endAngle,
bool anticlockwise);
- void addArcTo(const QPointF& p1, const QPointF& p2, float radius);
+ void addArcTo(const QPointF& p1, const QPointF& p2, qreal radius);
bool isPointInPath(qreal x, qreal y) const;
diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp
index e111f908d0..71a5e414f6 100644
--- a/src/quick/items/context2d/qquickcontext2dtexture.cpp
+++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp
@@ -120,8 +120,15 @@ void QQuickContext2DTexture::setItem(QQuickCanvasItem* item)
bool QQuickContext2DTexture::setCanvasWindow(const QRect& r)
{
- qreal canvasDevicePixelRatio = (m_item && m_item->window()) ?
- m_item->window()->effectiveDevicePixelRatio() : qApp->devicePixelRatio();
+ bool ok = false;
+ static qreal overriddenDevicePixelRatio =
+ !qEnvironmentVariableIsEmpty("QT_CANVAS_OVERRIDE_DEVICEPIXELRATIO") ?
+ qgetenv("QT_CANVAS_OVERRIDE_DEVICEPIXELRATIO").toFloat(&ok) : 0.0;
+ qreal canvasDevicePixelRatio = overriddenDevicePixelRatio;
+ if (overriddenDevicePixelRatio == 0.0) {
+ canvasDevicePixelRatio = (m_item && m_item->window()) ?
+ m_item->window()->effectiveDevicePixelRatio() : qApp->devicePixelRatio();
+ }
if (!qFuzzyCompare(m_canvasDevicePixelRatio, canvasDevicePixelRatio)) {
qCDebug(lcCanvas, "%s device pixel ratio %.1lf -> %.1lf",
(m_item->objectName().isEmpty() ? "Canvas" : qPrintable(m_item->objectName())),
diff --git a/src/quick/items/qquickaccessibleattached.cpp b/src/quick/items/qquickaccessibleattached.cpp
index 0b593c0089..f63ab764f1 100644
--- a/src/quick/items/qquickaccessibleattached.cpp
+++ b/src/quick/items/qquickaccessibleattached.cpp
@@ -109,7 +109,7 @@ QT_BEGIN_NAMESPACE
This property sets an accessible description.
Similar to the name it describes the item. The description
can be a little more verbose and tell what the item does,
- for example the functionallity of the button it describes.
+ for example the functionality of the button it describes.
*/
/*!
@@ -406,9 +406,10 @@ void QQuickAccessibleAttached::setRole(QAccessible::Role role)
m_state.focusable = true;
break;
case QAccessible::StaticText:
- if (!m_stateExplicitlySet.readOnly) {
+ if (!m_stateExplicitlySet.readOnly)
m_state.readOnly = true;
- }
+ if (!m_stateExplicitlySet.focusable)
+ m_state.focusable = true;
break;
default:
break;
diff --git a/src/quick/items/qquickaccessibleattached_p.h b/src/quick/items/qquickaccessibleattached_p.h
index 6d7d46bfd4..13f4b1f52f 100644
--- a/src/quick/items/qquickaccessibleattached_p.h
+++ b/src/quick/items/qquickaccessibleattached_p.h
@@ -189,7 +189,7 @@ public:
bool doAction(const QString &actionName);
void availableActions(QStringList *actions) const;
- Q_INVOKABLE static QString stripHtml(const QString &html);
+ Q_REVISION(6, 2) Q_INVOKABLE static QString stripHtml(const QString &html);
public Q_SLOTS:
void valueChanged() {
diff --git a/src/quick/items/qquickanchors.cpp b/src/quick/items/qquickanchors.cpp
index 8ee4229013..a2657c19f4 100644
--- a/src/quick/items/qquickanchors.cpp
+++ b/src/quick/items/qquickanchors.cpp
@@ -1409,5 +1409,7 @@ bool QQuickAnchorsPrivate::checkVAnchorValid(QQuickAnchorLine anchor) const
QT_END_NAMESPACE
+#include "moc_qquickanchors_p_p.cpp"
+
#include <moc_qquickanchors_p.cpp>
diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp
index 5b3d9db091..a3f2494fba 100644
--- a/src/quick/items/qquickanimatedsprite.cpp
+++ b/src/quick/items/qquickanimatedsprite.cpp
@@ -894,7 +894,7 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGSpriteNode *node)
maybeUpdate();
}
- qreal frameCount = d->m_spriteEngine->spriteFrames();
+ int frameCount = d->m_spriteEngine->spriteFrames();
bool reverse = d->m_spriteEngine->sprite()->reverse();
if (reverse)
frameAt = (frameCount - 1) - frameAt;
@@ -931,15 +931,15 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGSpriteNode *node)
x2 = x1 - w;
y2 = y1;
} else {
- x2 = 1.0 - w;
+ x2 = d->m_sheetSize.width() - w;
y2 = y1 - h;
- if (y2 < 0.0) {
+ if (y2 < 0) {
//the last row may not fill the entire width
int maxRowFrames = d->m_sheetSize.width() / d->m_spriteEngine->spriteWidth();
if (d->m_spriteEngine->maxFrames() % maxRowFrames)
x2 = ((d->m_spriteEngine->maxFrames() % maxRowFrames) - 1) * w;
- y2 = 1.0 - h;
+ y2 = d->m_sheetSize.height() - h;
}
}
} else {
@@ -947,10 +947,10 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGSpriteNode *node)
x2 = x1 + w;
y2 = y1;
} else {
- x2 = 0.0;
+ x2 = 0;
y2 = y1 + h;
- if (y2 >= 1.0)
- y2 = 0.0;
+ if (y2 >= d->m_sheetSize.height())
+ y2 = 0;
}
}
diff --git a/src/quick/items/qquickcolorgroup.cpp b/src/quick/items/qquickcolorgroup.cpp
index ebbbf07499..2a039ec1b4 100644
--- a/src/quick/items/qquickcolorgroup.cpp
+++ b/src/quick/items/qquickcolorgroup.cpp
@@ -47,6 +47,8 @@
QT_BEGIN_NAMESPACE
/*!
+ \internal
+
\class QQuickColorGroup
\brief The QQuickColorGroup class represents a set of colors.
\inmodule QtQuick
@@ -592,3 +594,5 @@ void QQuickColorGroup::resetColor(QPalette::ColorRole role, Notifier notifier)
}
QT_END_NAMESPACE
+
+#include "moc_qquickcolorgroup_p.cpp"
diff --git a/src/quick/items/qquickdrag.cpp b/src/quick/items/qquickdrag.cpp
index e6ffcee2aa..ba81028822 100644
--- a/src/quick/items/qquickdrag.cpp
+++ b/src/quick/items/qquickdrag.cpp
@@ -482,7 +482,9 @@ void QQuickDragAttached::setKeys(const QStringList &keys)
\qmlattachedproperty stringlist QtQuick::Drag::mimeData
\since 5.2
- This property holds a map of mimeData that is used during startDrag.
+ This property holds a map from mime type to data that is used during startDrag.
+ The mime data needs to be a \c string, or an \c ArrayBuffer with the data encoded
+ according to the mime type.
*/
QVariantMap QQuickDragAttached::mimeData() const
@@ -767,8 +769,12 @@ Qt::DropAction QQuickDragAttachedPrivate::startDrag(Qt::DropActions supportedAct
QDrag *drag = new QDrag(source ? source : q);
QMimeData *mimeData = new QMimeData();
- for (auto it = externalMimeData.cbegin(), end = externalMimeData.cend(); it != end; ++it)
- mimeData->setData(it.key(), it.value().toString().toUtf8());
+ for (auto it = externalMimeData.cbegin(), end = externalMimeData.cend(); it != end; ++it) {
+ if (it.value().typeId() == QMetaType::QByteArray)
+ mimeData->setData(it.key(), it.value().toByteArray());
+ else
+ mimeData->setData(it.key(), it.value().toString().toUtf8());
+ }
drag->setMimeData(mimeData);
if (pixmapLoader.isReady()) {
diff --git a/src/quick/items/qquickdrag_p.h b/src/quick/items/qquickdrag_p.h
index b02a0ea2e1..38ec278adb 100644
--- a/src/quick/items/qquickdrag_p.h
+++ b/src/quick/items/qquickdrag_p.h
@@ -109,9 +109,12 @@ public:
void grab(QQuickItem *item) { m_items.insert(new Item(item)); }
iterator release(iterator at) { Item *item = *at; at = at.erase(); delete item; return at; }
+ auto& ignoreList() { return m_ignoreDragItems; }
+
private:
ItemList m_items;
+ QVarLengthArray<QQuickItem *, 4> m_ignoreDragItems;
QObject *m_target;
};
diff --git a/src/quick/items/qquickdroparea.cpp b/src/quick/items/qquickdroparea.cpp
index 21dc0979a9..077574cbd1 100644
--- a/src/quick/items/qquickdroparea.cpp
+++ b/src/quick/items/qquickdroparea.cpp
@@ -226,7 +226,7 @@ void QQuickDropArea::dragMoveEvent(QDragMoveEvent *event)
emit d->drag->positionChanged();
event->accept();
- QQuickDropEvent dragTargetEvent(d, event);
+ QQuickDragEvent dragTargetEvent(d, event);
emit positionChanged(&dragTargetEvent);
}
@@ -276,7 +276,7 @@ void QQuickDropArea::dragEnterEvent(QDragEnterEvent *event)
event->accept();
- QQuickDropEvent dragTargetEvent(d, event);
+ QQuickDragEvent dragTargetEvent(d, event);
emit entered(&dragTargetEvent);
if (!event->isAccepted())
return;
@@ -325,7 +325,7 @@ void QQuickDropArea::dropEvent(QDropEvent *event)
if (!d->containsDrag)
return;
- QQuickDropEvent dragTargetEvent(d, event);
+ QQuickDragEvent dragTargetEvent(d, event);
emit dropped(&dragTargetEvent);
d->containsDrag = false;
@@ -337,7 +337,7 @@ void QQuickDropArea::dropEvent(QDropEvent *event)
/*!
\qmltype DragEvent
- \instantiates QQuickDropEvent
+ \instantiates QQuickDragEvent
\inqmlmodule QtQuick
\ingroup qtquick-input-events
\brief Provides information about a drag event.
@@ -514,7 +514,7 @@ void QQuickDropArea::dropEvent(QDropEvent *event)
easily be translated into a QByteArray. \a format should be one contained in the \l formats property.
*/
-QObject *QQuickDropEvent::source() const
+QObject *QQuickDragEvent::source() const
{
if (const QQuickDragMimeData *dragMime = qobject_cast<const QQuickDragMimeData *>(event->mimeData()))
return dragMime->source();
@@ -522,57 +522,57 @@ QObject *QQuickDropEvent::source() const
return event->source();
}
-QStringList QQuickDropEvent::keys() const
+QStringList QQuickDragEvent::keys() const
{
return d->getKeys(event->mimeData());
}
-bool QQuickDropEvent::hasColor() const
+bool QQuickDragEvent::hasColor() const
{
return event->mimeData()->hasColor();
}
-bool QQuickDropEvent::hasHtml() const
+bool QQuickDragEvent::hasHtml() const
{
return event->mimeData()->hasHtml();
}
-bool QQuickDropEvent::hasText() const
+bool QQuickDragEvent::hasText() const
{
return event->mimeData()->hasText();
}
-bool QQuickDropEvent::hasUrls() const
+bool QQuickDragEvent::hasUrls() const
{
return event->mimeData()->hasUrls();
}
-QVariant QQuickDropEvent::colorData() const
+QVariant QQuickDragEvent::colorData() const
{
return event->mimeData()->colorData();
}
-QString QQuickDropEvent::html() const
+QString QQuickDragEvent::html() const
{
return event->mimeData()->html();
}
-QString QQuickDropEvent::text() const
+QString QQuickDragEvent::text() const
{
return event->mimeData()->text();
}
-QList<QUrl> QQuickDropEvent::urls() const
+QList<QUrl> QQuickDragEvent::urls() const
{
return event->mimeData()->urls();
}
-QStringList QQuickDropEvent::formats() const
+QStringList QQuickDragEvent::formats() const
{
return event->mimeData()->formats();
}
-void QQuickDropEvent::getDataAsString(QQmlV4Function *args)
+void QQuickDragEvent::getDataAsString(QQmlV4Function *args)
{
if (args->length() != 0) {
QV4::ExecutionEngine *v4 = args->v4engine();
@@ -584,7 +584,7 @@ void QQuickDropEvent::getDataAsString(QQmlV4Function *args)
}
}
-void QQuickDropEvent::getDataAsArrayBuffer(QQmlV4Function *args)
+void QQuickDragEvent::getDataAsArrayBuffer(QQmlV4Function *args)
{
if (args->length() != 0) {
QV4::ExecutionEngine *v4 = args->v4engine();
@@ -595,12 +595,12 @@ void QQuickDropEvent::getDataAsArrayBuffer(QQmlV4Function *args)
}
}
-void QQuickDropEvent::acceptProposedAction(QQmlV4Function *)
+void QQuickDragEvent::acceptProposedAction(QQmlV4Function *)
{
event->acceptProposedAction();
}
-void QQuickDropEvent::accept(QQmlV4Function *args)
+void QQuickDragEvent::accept(QQmlV4Function *args)
{
Qt::DropAction action = event->dropAction();
diff --git a/src/quick/items/qquickdroparea_p.h b/src/quick/items/qquickdroparea_p.h
index 74f16df9cc..122029a18a 100644
--- a/src/quick/items/qquickdroparea_p.h
+++ b/src/quick/items/qquickdroparea_p.h
@@ -61,7 +61,7 @@ QT_REQUIRE_CONFIG(quick_draganddrop);
QT_BEGIN_NAMESPACE
class QQuickDropAreaPrivate;
-class QQuickDropEvent : public QObject
+class QQuickDragEvent : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal x READ x)
@@ -81,10 +81,11 @@ class QQuickDropEvent : public QObject
Q_PROPERTY(QString text READ text)
Q_PROPERTY(QList<QUrl> urls READ urls)
Q_PROPERTY(QStringList formats READ formats)
- QML_ANONYMOUS
+ QML_NAMED_ELEMENT(DragEvent)
+ QML_UNCREATABLE("DragEvent is only meant to be created by DropArea")
QML_ADDED_IN_VERSION(2, 0)
public:
- QQuickDropEvent(QQuickDropAreaPrivate *d, QDropEvent *event) : d(d), event(event) {}
+ QQuickDragEvent(QQuickDropAreaPrivate *d, QDropEvent *event) : d(d), event(event) {}
qreal x() const { return event->position().x(); }
qreal y() const { return event->position().y(); }
@@ -176,10 +177,10 @@ Q_SIGNALS:
void keysChanged();
void sourceChanged();
- void entered(QQuickDropEvent *drag);
+ void entered(QQuickDragEvent *drag);
void exited();
- void positionChanged(QQuickDropEvent *drag);
- void dropped(QQuickDropEvent *drop);
+ void positionChanged(QQuickDragEvent *drag);
+ void dropped(QQuickDragEvent *drop);
protected:
void dragMoveEvent(QDragMoveEvent *event) override;
@@ -194,7 +195,6 @@ private:
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickDropEvent)
QML_DECLARE_TYPE(QQuickDropArea)
#endif // QQUICKDROPAREA_P_H
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index ba9082cc2a..f9b5b80a92 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -289,6 +289,7 @@ bool QQuickKeyEvent::matches(QKeySequence::StandardKey matchKey) const
/*!
\qmlproperty int QtQuick::MouseEvent::source
\since 5.7
+ \deprecated [6.2] Use \l {Qt Quick Input Handlers}{input handlers} with \l {PointerDeviceHandler::acceptedDevices}{acceptedDevices} set.
This property holds the source of the mouse event.
@@ -364,10 +365,9 @@ bool QQuickKeyEvent::matches(QKeySequence::StandardKey matchKey) const
\ingroup qtquick-input-events
\brief Provides information about a mouse wheel event.
- The position of the mouse can be found via the
- \l {Item::x} {x} and \l {Item::y} {y} properties.
+ The position of the mouse can be found via the \l x and \l y properties.
- \sa MouseArea
+ \sa WheelHandler, MouseArea
*/
/*!
@@ -380,16 +380,20 @@ bool QQuickKeyEvent::matches(QKeySequence::StandardKey matchKey) const
\qmlproperty real QtQuick::WheelEvent::y
These properties hold the coordinates of the position supplied by the wheel event.
+
+ \sa QWheelEvent::position()
*/
/*!
\qmlproperty bool QtQuick::WheelEvent::accepted
- Setting \a accepted to true prevents the wheel event from being
- propagated to items below this item.
+ Setting \a accepted to \c true prevents the wheel event from being
+ propagated to items below the receiving item or handler.
- Generally, if the item acts on the wheel event then it should be accepted
+ Generally, if the item acts on the wheel event, it should be accepted
so that items lower in the stacking order do not also respond to the same event.
+
+ \sa QWheelEvent::accepted
*/
/*!
@@ -403,32 +407,39 @@ bool QQuickKeyEvent::matches(QKeySequence::StandardKey matchKey) const
\li \l {Qt::RightButton} {Qt.RightButton}
\li \l {Qt::MiddleButton} {Qt.MiddleButton}
\endlist
+
+ \sa QWheelEvent::buttons()
*/
/*!
\qmlproperty point QtQuick::WheelEvent::angleDelta
- This property holds the distance that the wheel is rotated in wheel degrees.
- The x and y cordinate of this property holds the delta in horizontal and
- vertical orientation.
+ This property holds the relative amount that the wheel was rotated, in
+ eighths of a degree. The \c x and \c y coordinates of this property hold
+ the delta in horizontal and vertical orientations, respectively.
A positive value indicates that the wheel was rotated up/right;
a negative value indicates that the wheel was rotated down/left.
- Most mouse types work in steps of 15 degrees, in which case the delta value is a
- multiple of 120; i.e., 120 units * 1/8 = 15 degrees.
+ Most mouse types work in steps of \c 15 degrees, in which case the delta value is a
+ multiple of \c 120; i.e., \c {120 units * 1/8 = 15 degrees}.
+
+ \sa QWheelEvent::angleDelta()
*/
/*!
\qmlproperty point QtQuick::WheelEvent::pixelDelta
This property holds the delta in screen pixels and is available in platforms that
- have high-resolution trackpads, such as \macos.
- The x and y cordinate of this property holds the delta in horizontal and
- vertical orientation. The value should be used directly to scroll content on screen.
+ have high-resolution \l {QInputDevice::DeviceType::TouchPad}{trackpads}, such as \macos.
+ The \c x and \c y coordinates of this property hold the delta in horizontal
+ and vertical orientations, respectively. The values can be used directly to
+ scroll content on screen.
- For platforms without high-resolution trackpad support, pixelDelta will always be (0,0),
- and angleDelta should be used instead.
+ For platforms without \l {QInputDevice::Capability::PixelScroll}{high-resolution trackpad}
+ support, pixelDelta will always be \c {(0,0)}, and \l angleDelta should be used instead.
+
+ \sa QWheelEvent::pixelDelta()
*/
/*!
@@ -449,7 +460,7 @@ bool QQuickKeyEvent::matches(QKeySequence::StandardKey matchKey) const
For example, to react to a Control key pressed during the wheel event:
\qml
- MouseArea {
+ WheelHandler {
onWheel: (wheel)=> {
if (wheel.modifiers & Qt.ControlModifier) {
adjustZoom(wheel.angleDelta.y / 120);
@@ -457,6 +468,8 @@ bool QQuickKeyEvent::matches(QKeySequence::StandardKey matchKey) const
}
}
\endqml
+
+ \sa QWheelEvent::modifiers()
*/
/*!
@@ -478,7 +491,11 @@ bool QQuickKeyEvent::matches(QKeySequence::StandardKey matchKey) const
negate the angleDelta or pixelDelta values.
\note Many platforms provide no such information. On such platforms
- \l inverted always returns false.
+ \c inverted always returns \c false.
+
+ \sa QWheelEvent::inverted()
*/
QT_END_NAMESPACE
+
+#include "moc_qquickevents_p_p.cpp"
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 14bf190fe9..e050561ac5 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -43,6 +43,8 @@
#include "qquickwindow.h"
#include "qquickwindow_p.h"
#include "qquickevents_p_p.h"
+#include "qquickmousearea_p.h"
+#include "qquickdrag_p.h"
#include <QtQuick/private/qquickpointerhandler_p.h>
#include <QtQuick/private/qquicktransition_p.h>
@@ -52,6 +54,7 @@
#include <QtGui/qevent.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/private/qeventpoint_p.h>
#include <QtGui/qstylehints.h>
#include <QtCore/qmath.h>
#include "qplatformdefs.h"
@@ -137,7 +140,8 @@ void QQuickFlickableVisibleArea::updateVisible()
qreal pagePos = 0;
qreal pageSize = 0;
if (!qFuzzyIsNull(maxYBounds)) {
- pagePos = (-p->vData.move.value() + flickable->minYExtent()) / maxYBounds;
+ qreal y = p->pixelAligned ? Round(p->vData.move.value()) : p->vData.move.value();
+ pagePos = (-y + flickable->minYExtent()) / maxYBounds;
pageSize = viewheight / maxYBounds;
}
@@ -155,7 +159,8 @@ void QQuickFlickableVisibleArea::updateVisible()
const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent();
const qreal maxXBounds = maxxextent + viewwidth;
if (!qFuzzyIsNull(maxXBounds)) {
- pagePos = (-p->hData.move.value() + flickable->minXExtent()) / maxXBounds;
+ qreal x = p->pixelAligned ? Round(p->hData.move.value()) : p->hData.move.value();
+ pagePos = (-x + flickable->minXExtent()) / maxXBounds;
pageSize = viewwidth / maxXBounds;
} else {
pagePos = 0;
@@ -253,9 +258,27 @@ QQuickFlickablePrivate::AxisData::~AxisData()
delete transitionToBounds;
}
+class QQuickFlickableContentItem : public QQuickItem
+{
+ /*!
+ \internal
+ The flickable area inside the viewport can be bigger than the bounds of the
+ content item itself, if the flickable is using non-zero extents (as returned
+ by e.g minXExtent()). Since the default implementation in QQuickItem::contains()
+ only checks if the point is inside the bounds of the item, we need to override it
+ to check the extents as well. The easist way to do this is to simply check if the
+ point is inside the bounds of the flickable rather than the content item.
+ */
+ bool contains(const QPointF &point) const override
+ {
+ const QQuickItem *flickable = parentItem();
+ const QPointF posInFlickable = flickable->mapFromItem(this, point);
+ return flickable->contains(posInFlickable);
+ }
+};
QQuickFlickablePrivate::QQuickFlickablePrivate()
- : contentItem(new QQuickItem)
+ : contentItem(new QQuickFlickableContentItem)
, hData(this, &QQuickFlickablePrivate::setViewportX)
, vData(this, &QQuickFlickablePrivate::setViewportY)
, hMoved(false), vMoved(false)
@@ -293,16 +316,17 @@ void QQuickFlickablePrivate::init()
viewportPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
}
-/*
- Returns the amount to overshoot by given a velocity.
- Will be roughly in range 0 - size/4
+/*!
+ \internal
+ Returns the distance to overshoot, given \a velocity.
+ Will be in range 0 - velocity / 3, but limited to a max of QML_FLICK_OVERSHOOT
*/
-qreal QQuickFlickablePrivate::overShootDistance(qreal size) const
+qreal QQuickFlickablePrivate::overShootDistance(qreal velocity) const
{
if (maxVelocity <= 0)
- return 0.0;
+ return 0;
- return qMin(qreal(QML_FLICK_OVERSHOOT), size/3);
+ return qMin(qreal(QML_FLICK_OVERSHOOT), velocity / 3);
}
void QQuickFlickablePrivate::AxisData::addVelocitySample(qreal v, qreal maxVelocity)
@@ -329,7 +353,7 @@ void QQuickFlickablePrivate::AxisData::updateVelocity()
}
}
-void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &)
+void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &oldGeom)
{
Q_Q(QQuickFlickable);
if (item == contentItem) {
@@ -338,8 +362,14 @@ void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometr
orient |= Qt::Horizontal;
if (change.yChange())
orient |= Qt::Vertical;
- if (orient)
+ if (orient) {
q->viewportMoved(orient);
+ const QPointF deltaMoved = item->position() - oldGeom.topLeft();
+ if (hData.contentPositionChangedExternallyDuringDrag)
+ hData.pressPos += deltaMoved.x();
+ if (vData.contentPositionChangedExternallyDuringDrag)
+ vData.pressPos += deltaMoved.y();
+ }
if (orient & Qt::Horizontal)
emit q->contentXChanged();
if (orient & Qt::Vertical)
@@ -531,8 +561,8 @@ void QQuickFlickablePrivate::updateBeginningEnd()
const qreal maxyextent = -q->maxYExtent();
const qreal minyextent = -q->minYExtent();
const qreal ypos = -vData.move.value();
- bool atBeginning = fuzzyLessThanOrEqualTo(ypos, minyextent);
- bool atEnd = fuzzyLessThanOrEqualTo(maxyextent, ypos);
+ bool atBeginning = fuzzyLessThanOrEqualTo(ypos, std::ceil(minyextent));
+ bool atEnd = fuzzyLessThanOrEqualTo(std::floor(maxyextent), ypos);
if (atBeginning != vData.atBeginning) {
vData.atBeginning = atBeginning;
@@ -551,8 +581,8 @@ void QQuickFlickablePrivate::updateBeginningEnd()
const qreal maxxextent = -q->maxXExtent();
const qreal minxextent = -q->minXExtent();
const qreal xpos = -hData.move.value();
- atBeginning = fuzzyLessThanOrEqualTo(xpos, minxextent);
- atEnd = fuzzyLessThanOrEqualTo(maxxextent, xpos);
+ atBeginning = fuzzyLessThanOrEqualTo(xpos, std::ceil(minxextent));
+ atEnd = fuzzyLessThanOrEqualTo(std::floor(maxxextent), xpos);
if (atBeginning != hData.atBeginning) {
hData.atBeginning = atBeginning;
@@ -722,7 +752,8 @@ void QQuickFlickablePrivate::updateBeginningEnd()
/*!
\qmlsignal QtQuick::Flickable::flickEnded()
- This signal is emitted when the view stops moving due to a flick.
+ This signal is emitted when the view stops moving after a flick
+ or a series of flicks.
*/
/*!
@@ -799,8 +830,11 @@ void QQuickFlickable::setContentX(qreal pos)
d->hData.vTime = d->timeline.time();
if (isMoving() || isFlicking())
movementEnding(true, false);
- if (!qFuzzyCompare(-pos, d->hData.move.value()))
+ if (!qFuzzyCompare(-pos, d->hData.move.value())) {
+ d->hData.contentPositionChangedExternallyDuringDrag = d->hData.dragging;
d->hData.move.setValue(-pos);
+ d->hData.contentPositionChangedExternallyDuringDrag = false;
+ }
}
qreal QQuickFlickable::contentY() const
@@ -817,8 +851,11 @@ void QQuickFlickable::setContentY(qreal pos)
d->vData.vTime = d->timeline.time();
if (isMoving() || isFlicking())
movementEnding(false, true);
- if (!qFuzzyCompare(-pos, d->vData.move.value()))
+ if (!qFuzzyCompare(-pos, d->vData.move.value())) {
+ d->vData.contentPositionChangedExternallyDuringDrag = d->vData.dragging;
d->vData.move.setValue(-pos);
+ d->vData.contentPositionChangedExternallyDuringDrag = false;
+ }
}
/*!
@@ -1032,6 +1069,17 @@ void QQuickFlickable::setSynchronousDrag(bool v)
}
}
+/*! \internal
+ Take the velocity of the first point from the given \a event and transform
+ it to the local coordinate system (taking scale and rotation into account).
+*/
+QVector2D QQuickFlickablePrivate::firstPointLocalVelocity(QPointerEvent *event)
+{
+ QTransform transform = windowToItemTransform();
+ // rotate and scale the velocity vector from scene to local
+ return QVector2D(transform.map(event->point(0).velocity().toPointF()) - transform.map(QPointF()));
+}
+
qint64 QQuickFlickablePrivate::computeCurrentTime(QInputEvent *event) const
{
if (0 != event->timestamp())
@@ -1103,7 +1151,9 @@ void QQuickFlickablePrivate::maybeBeginDrag(qint64 currentTimestamp, const QPoin
pressPos = pressPosn;
hData.pressPos = hData.move.value();
vData.pressPos = vData.move.value();
- bool wasFlicking = hData.flicking || vData.flicking;
+ const bool wasFlicking = hData.flicking || vData.flicking;
+ hData.flickingWhenDragBegan = hData.flicking;
+ vData.flickingWhenDragBegan = vData.flicking;
if (hData.flicking) {
hData.flicking = false;
emit q->flickingHorizontallyChanged();
@@ -1333,15 +1383,15 @@ void QQuickFlickablePrivate::handleMoveEvent(QPointerEvent *event)
{
Q_Q(QQuickFlickable);
if (!interactive || lastPosTime == -1 ||
- (event->isSinglePointEvent() && static_cast<QSinglePointEvent *>(event)->buttons() == Qt::NoButton))
+ (event->isSinglePointEvent() && !static_cast<QSinglePointEvent *>(event)->buttons().testFlag(Qt::LeftButton)))
return;
qint64 currentTimestamp = computeCurrentTime(event);
const auto &firstPoint = event->points().first();
const auto &pos = firstPoint.position();
- QVector2D deltas = QVector2D(pos - q->mapFromGlobal(firstPoint.globalPressPosition()));
+ const QVector2D deltas = QVector2D(pos - q->mapFromGlobal(firstPoint.globalPressPosition()));
+ const QVector2D velocity = firstPointLocalVelocity(event);
bool overThreshold = false;
- QVector2D velocity = event->point(0).velocity();
if (q->yflick())
overThreshold |= QQuickDeliveryAgentPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, firstPoint);
@@ -1373,12 +1423,14 @@ void QQuickFlickablePrivate::handleReleaseEvent(QPointerEvent *event)
bool canBoost = false;
const auto pos = event->points().first().position();
- const auto pressPos = event->points().first().pressPosition();
+ const auto pressPos = q->mapFromGlobal(event->points().first().globalPressPosition());
+ const QVector2D eventVelocity = firstPointLocalVelocity(event);
+ qCDebug(lcVel) << event->deviceType() << event->type() << "velocity" << event->points().first().velocity() << "transformed to local" << eventVelocity;
qreal vVelocity = 0;
if (elapsed < 100 && vData.velocity != 0.) {
vVelocity = (event->device()->capabilities().testFlag(QInputDevice::Capability::Velocity)
- ? event->point(0).velocity().y() : vData.velocity);
+ ? eventVelocity.y() : vData.velocity);
}
if ((vData.atBeginning && vVelocity > 0.) || (vData.atEnd && vVelocity < 0.)) {
vVelocity /= 2;
@@ -1393,7 +1445,7 @@ void QQuickFlickablePrivate::handleReleaseEvent(QPointerEvent *event)
qreal hVelocity = 0;
if (elapsed < 100 && hData.velocity != 0.) {
hVelocity = (event->device()->capabilities().testFlag(QInputDevice::Capability::Velocity)
- ? event->point(0).velocity().x() : hData.velocity);
+ ? eventVelocity.x() : hData.velocity);
}
if ((hData.atBeginning && hVelocity > 0.) || (hData.atEnd && hVelocity < 0.)) {
hVelocity /= 2;
@@ -1599,7 +1651,7 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event)
return;
}
- if (event->source() == Qt::MouseEventNotSynthesized || event->pixelDelta().isNull()) {
+ if (event->source() == Qt::MouseEventNotSynthesized || event->pixelDelta().isNull() || event->phase() == Qt::NoScrollPhase) {
// no pixel delta (physical mouse wheel, or "dumb" touchpad), so use angleDelta
int xDelta = event->angleDelta().x();
int yDelta = event->angleDelta().y();
@@ -1620,9 +1672,9 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event)
d->vData.addVelocitySample(instVelocity, d->maxVelocity);
d->vData.updateVelocity();
if ((yDelta > 0 && contentY() > -minYExtent()) || (yDelta < 0 && contentY() < -maxYExtent())) {
- d->flickY(d->vData.velocity);
- d->flickingStarted(false, true);
- if (d->vData.flicking) {
+ const bool newFlick = d->flickY(d->vData.velocity);
+ if (newFlick && (d->vData.atBeginning != (yDelta > 0) || d->vData.atEnd != (yDelta < 0))) {
+ d->flickingStarted(false, true);
d->vMoved = true;
movementStarting();
}
@@ -1637,9 +1689,9 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event)
d->hData.addVelocitySample(instVelocity, d->maxVelocity);
d->hData.updateVelocity();
if ((xDelta > 0 && contentX() > -minXExtent()) || (xDelta < 0 && contentX() < -maxXExtent())) {
- d->flickX(d->hData.velocity);
- d->flickingStarted(true, false);
- if (d->hData.flicking) {
+ const bool newFlick = d->flickX(d->hData.velocity);
+ if (newFlick && (d->hData.atBeginning != (xDelta > 0) || d->hData.atEnd != (xDelta < 0))) {
+ d->flickingStarted(true, false);
d->hMoved = true;
movementStarting();
}
@@ -1867,13 +1919,13 @@ void QQuickFlickable::viewportMoved(Qt::Orientations orient)
{
Q_D(QQuickFlickable);
if (orient & Qt::Vertical)
- d->viewportAxisMoved(d->vData, minYExtent(), maxYExtent(), height(), d->fixupY_callback);
+ d->viewportAxisMoved(d->vData, minYExtent(), maxYExtent(), d->fixupY_callback);
if (orient & Qt::Horizontal)
- d->viewportAxisMoved(d->hData, minXExtent(), maxXExtent(), width(), d->fixupX_callback);
+ d->viewportAxisMoved(d->hData, minXExtent(), maxXExtent(), d->fixupX_callback);
d->updateBeginningEnd();
}
-void QQuickFlickablePrivate::viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
+void QQuickFlickablePrivate::viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent,
QQuickTimeLineCallback::Callback fixupCallback)
{
if (!scrollingPhase && (pressed || calcVelocity)) {
@@ -1889,10 +1941,15 @@ void QQuickFlickablePrivate::viewportAxisMoved(AxisData &data, qreal minExtent,
} else {
if (timeline.time() > data.vTime) {
velocityTimeline.reset(data.smoothVelocity);
- qreal velocity = (data.lastPos - data.move.value()) * 1000 / (timeline.time() - data.vTime);
- if (!qFuzzyCompare(data.smoothVelocity.value(), velocity))
- qCDebug(lcVel) << "velocity" << data.smoothVelocity.value() << "->" << velocity;
- data.smoothVelocity.setValue(velocity);
+ int dt = timeline.time() - data.vTime;
+ if (dt > 2) {
+ qreal velocity = (data.lastPos - data.move.value()) * 1000 / dt;
+ if (!qFuzzyCompare(data.smoothVelocity.value(), velocity))
+ qCDebug(lcVel) << "velocity" << data.smoothVelocity.value() << "->" << velocity
+ << "computed as (" << data.lastPos << "-" << data.move.value() << ") * 1000 / ("
+ << timeline.time() << "-" << data.vTime << ")";
+ data.smoothVelocity.setValue(velocity);
+ }
}
}
@@ -1904,7 +1961,7 @@ void QQuickFlickablePrivate::viewportAxisMoved(AxisData &data, qreal minExtent,
? data.move.value() - minExtent
: maxExtent - data.move.value();
data.inOvershoot = true;
- qreal maxDistance = overShootDistance(vSize) - overBound;
+ qreal maxDistance = overShootDistance(qAbs(data.smoothVelocity.value())) - overBound;
resetTimeline(data);
if (maxDistance > 0)
timeline.accel(data.move, -data.smoothVelocity.value(), deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
@@ -2513,6 +2570,23 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev
bool receiverDisabled = receiver && !receiver->isEnabled();
bool stealThisEvent = d->stealMouse;
bool receiverKeepsGrab = receiver && (receiver->keepMouseGrab() || receiver->keepTouchGrab());
+ bool receiverRelinquishGrab = false;
+
+ // Special case for MouseArea, try to guess what it does with the event
+ if (auto *mouseArea = qmlobject_cast<QQuickMouseArea *>(receiver)) {
+ bool preventStealing = mouseArea->preventStealing();
+ if (mouseArea->drag() && mouseArea->drag()->target())
+ preventStealing = true;
+ if (!preventStealing && receiverKeepsGrab) {
+ receiverRelinquishGrab = !receiverDisabled
+ || (QQuickDeliveryAgentPrivate::isMouseEvent(event)
+ && firstPoint.state() == QEventPoint::State::Pressed
+ && (receiver->acceptedMouseButtons() & static_cast<QMouseEvent *>(event)->button()));
+ if (receiverRelinquishGrab)
+ receiverKeepsGrab = false;
+ }
+ }
+
if ((stealThisEvent || contains(localPos)) && (!receiver || !receiverKeepsGrab || receiverDisabled)) {
QScopedPointer<QPointerEvent> localizedEvent(QQuickDeliveryAgentPrivate::clonePointerEvent(event, localPos));
localizedEvent->setAccepted(false);
@@ -2523,7 +2597,9 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev
case QEventPoint::State::Pressed:
d->handlePressEvent(localizedEvent.data());
d->captureDelayedPress(receiver, event);
- stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
+ // never grab the pointing device on press during filtering: do it later, during a move
+ d->stealMouse = false;
+ stealThisEvent = false;
break;
case QEventPoint::State::Released:
d->handleReleaseEvent(localizedEvent.data());
@@ -2540,7 +2616,7 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev
event->setExclusiveGrabber(firstPoint, this);
}
- const bool filtered = stealThisEvent || d->delayedPressEvent || receiverDisabled;
+ const bool filtered = !receiverRelinquishGrab && (stealThisEvent || d->delayedPressEvent || receiverDisabled);
if (filtered) {
event->setAccepted(true);
}
@@ -2566,8 +2642,19 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev
bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
{
Q_D(QQuickFlickable);
+ QPointerEvent *pointerEvent = e->isPointerEvent() ? static_cast<QPointerEvent *>(e) : nullptr;
+
+ auto wantsPointerEvent_helper = [this, d, i, pointerEvent]() {
+ Q_ASSERT(pointerEvent);
+ QQuickDeliveryAgentPrivate::localizePointerEvent(pointerEvent, this);
+ const bool wants = d->wantsPointerEvent(pointerEvent);
+ // re-localize event back to \a i before returning
+ QQuickDeliveryAgentPrivate::localizePointerEvent(pointerEvent, i);
+ return wants;
+ };
+
if (!isVisible() || !isEnabled() || !isInteractive() ||
- (e->isPointerEvent() && !d->wantsPointerEvent(static_cast<QPointerEvent *>(e)))) {
+ (pointerEvent && !wantsPointerEvent_helper())) {
d->cancelInteraction();
return QQuickItem::childMouseEventFilter(i, e);
}
@@ -2579,8 +2666,8 @@ bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
qCDebug(lcFilter) << "filtering UngrabMouse" << spe->points().first() << "for" << i << "grabber is" << grabber;
if (grabber != this)
mouseUngrabEvent(); // A child has been ungrabbed
- } else if (e->isPointerEvent()) {
- return filterPointerEvent(i, static_cast<QPointerEvent *>(e));
+ } else if (pointerEvent) {
+ return filterPointerEvent(i, pointerEvent);
}
return QQuickItem::childMouseEventFilter(i, e);
@@ -2848,6 +2935,12 @@ void QQuickFlickable::movementStarting()
if (!wasMoving && (d->hData.moving || d->vData.moving)) {
emit movingChanged();
emit movementStarted();
+#if QT_CONFIG(accessibility)
+ if (QAccessible::isActive()) {
+ QAccessibleEvent ev(this, QAccessible::ScrollingStart);
+ QAccessible::updateAccessibility(&ev);
+ }
+#endif
}
}
@@ -2861,7 +2954,7 @@ void QQuickFlickable::movementEnding(bool hMovementEnding, bool vMovementEnding)
Q_D(QQuickFlickable);
// emit flicking signals
- bool wasFlicking = d->hData.flicking || d->vData.flicking;
+ const bool wasFlicking = d->hData.flicking || d->vData.flicking;
if (hMovementEnding && d->hData.flicking) {
d->hData.flicking = false;
emit flickingHorizontallyChanged();
@@ -2873,6 +2966,10 @@ void QQuickFlickable::movementEnding(bool hMovementEnding, bool vMovementEnding)
if (wasFlicking && (!d->hData.flicking || !d->vData.flicking)) {
emit flickingChanged();
emit flickEnded();
+ } else if (d->hData.flickingWhenDragBegan || d->vData.flickingWhenDragBegan) {
+ d->hData.flickingWhenDragBegan = !hMovementEnding;
+ d->vData.flickingWhenDragBegan = !vMovementEnding;
+ emit flickEnded();
}
// emit moving signals
@@ -2892,6 +2989,12 @@ void QQuickFlickable::movementEnding(bool hMovementEnding, bool vMovementEnding)
if (wasMoving && !isMoving()) {
emit movingChanged();
emit movementEnded();
+#if QT_CONFIG(accessibility)
+ if (QAccessible::isActive()) {
+ QAccessibleEvent ev(this, QAccessible::ScrollingEnd);
+ QAccessible::updateAccessibility(&ev);
+ }
+#endif
}
if (hMovementEnding) {
@@ -3019,4 +3122,6 @@ void QQuickFlickable::setBoundsMovement(BoundsMovement movement)
QT_END_NAMESPACE
+#include "moc_qquickflickable_p_p.cpp"
+
#include "moc_qquickflickable_p.cpp"
diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h
index a003a69b9f..c748bb9f43 100644
--- a/src/quick/items/qquickflickable_p_p.h
+++ b/src/quick/items/qquickflickable_p_p.h
@@ -107,8 +107,9 @@ public:
, smoothVelocity(fp), atEnd(false), atBeginning(true)
, transitionToSet(false)
, fixingUp(false), inOvershoot(false), inRebound(false), moving(false), flicking(false)
- , dragging(false), extentsChanged(false)
+ , flickingWhenDragBegan(false), dragging(false), extentsChanged(false)
, explicitValue(false), minExtentDirty(true), maxExtentDirty(true)
+ , contentPositionChangedExternallyDuringDrag(false)
, unused(0)
{}
@@ -119,6 +120,7 @@ public:
dragStartOffset = 0;
fixingUp = false;
inOvershoot = false;
+ contentPositionChangedExternallyDuringDrag = false;
}
void markExtentsDirty() {
@@ -156,20 +158,22 @@ public:
int vTime;
QQuickFlickablePrivate::Velocity smoothVelocity;
QPODVector<qreal,10> velocityBuffer;
- bool atEnd : 1;
- bool atBeginning : 1;
- bool transitionToSet : 1;
- bool fixingUp : 1;
- bool inOvershoot : 1;
- bool inRebound : 1;
- bool moving : 1;
- bool flicking : 1;
- bool dragging : 1;
- bool extentsChanged : 1;
- bool explicitValue : 1;
- mutable bool minExtentDirty : 1;
- mutable bool maxExtentDirty : 1;
- uint unused : 19;
+ uint atEnd : 1;
+ uint atBeginning : 1;
+ uint transitionToSet : 1;
+ uint fixingUp : 1;
+ uint inOvershoot : 1;
+ uint inRebound : 1;
+ uint moving : 1;
+ uint flicking : 1;
+ uint flickingWhenDragBegan : 1;
+ uint dragging : 1;
+ uint extentsChanged : 1;
+ uint explicitValue : 1;
+ mutable uint minExtentDirty : 1;
+ mutable uint maxExtentDirty : 1;
+ uint contentPositionChangedExternallyDuringDrag : 1;
+ uint unused : 17;
};
bool flickX(qreal velocity);
@@ -195,7 +199,7 @@ public:
void setViewportX(qreal x);
void setViewportY(qreal y);
- qreal overShootDistance(qreal size) const;
+ qreal overShootDistance(qreal velocity) const;
void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) override;
@@ -256,7 +260,7 @@ public:
QQuickFlickable::BoundsMovement boundsMovement;
QQuickTransition *rebound;
- void viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
+ void viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent,
QQuickTimeLineCallback::Callback fixupCallback);
void handlePressEvent(QPointerEvent *);
@@ -268,6 +272,7 @@ public:
const QVector2D &deltas, bool overThreshold, bool momentum,
bool velocitySensitiveOverBounds, const QVector2D &velocity);
+ QVector2D firstPointLocalVelocity(QPointerEvent *event);
qint64 computeCurrentTime(QInputEvent *event) const;
qreal devicePixelRatio() const;
@@ -278,7 +283,7 @@ public:
static void data_clear(QQmlListProperty<QObject> *);
};
-class QQuickFlickableVisibleArea : public QObject
+class Q_QUICK_PRIVATE_EXPORT QQuickFlickableVisibleArea : public QObject
{
Q_OBJECT
diff --git a/src/quick/items/qquickflipable.cpp b/src/quick/items/qquickflipable.cpp
index a338985622..87112c8b93 100644
--- a/src/quick/items/qquickflipable.cpp
+++ b/src/quick/items/qquickflipable.cpp
@@ -39,7 +39,7 @@
#include "qquickflipable_p.h"
#include "qquickitem_p.h"
-
+#include "qquicktranslate_p.h"
#include <QtQml/qqmlinfo.h>
@@ -237,9 +237,20 @@ void QQuickFlipable::updatePolish()
d->updateSide();
}
-// determination on the currently visible side of the flipable
-// has to be done on the complete scene transform to give
-// correct results.
+/*! \internal
+ Flipable must use the complete scene transform to correctly determine the
+ currently visible side.
+
+ It must also be independent of camera distance, in case the contents are
+ too wide: for rotation transforms we simply call QMatrix4x4::rotate(),
+ whereas QQuickRotation::applyTo(QMatrix4x4*) calls
+ QMatrix4x4::projectedRotate() which by default assumes the camera distance
+ is 1024 virtual pixels. So for example if contents inside Flipable are to
+ be flipped around the y axis, and are wider than 1024*2, some of the
+ rendering goes behind the "camera". That's expected for rendering (since we
+ didn't provide API to change camera distance), but not ok for deciding when
+ to flip.
+*/
void QQuickFlipablePrivate::updateSide()
{
Q_Q(QQuickFlipable);
@@ -249,8 +260,39 @@ void QQuickFlipablePrivate::updateSide()
sideDirty = false;
- QTransform sceneTransform;
- itemToParentTransform(sceneTransform);
+ QMatrix4x4 sceneTransform;
+
+ const qreal tx = x.value();
+ const qreal ty = y.value();
+ if (!qFuzzyIsNull(tx) || !qFuzzyIsNull(ty))
+ sceneTransform.translate(tx, ty);
+
+ for (const auto *transform : std::as_const(transforms)) {
+ if (const auto *rot = qobject_cast<const QQuickRotation *>(transform)) {
+ // rotation is a special case: we want to call rotate() instead of projectedRotate()
+ const auto angle = rot->angle();
+ const auto axis = rot->axis();
+ if (!(qFuzzyIsNull(angle) || axis.isNull())) {
+ sceneTransform.translate(rot->origin());
+ sceneTransform.rotate(angle, axis.x(), axis.y(), axis.z());
+ sceneTransform.translate(-rot->origin());
+ }
+ } else {
+ transform->applyTo(&sceneTransform);
+ }
+ }
+
+ const bool hasRotation = !qFuzzyIsNull(rotation());
+ const bool hasScale = !qFuzzyCompare(scale(), 1);
+ if (hasScale || hasRotation) {
+ QPointF tp = computeTransformOrigin();
+ sceneTransform.translate(tp.x(), tp.y());
+ if (hasScale)
+ sceneTransform.scale(scale(), scale());
+ if (hasRotation)
+ sceneTransform.rotate(rotation(), 0, 0, 1);
+ sceneTransform.translate(-tp.x(), -tp.y());
+ }
QPointF p1(0, 0);
QPointF p2(1, 0);
diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp
index 90fa05f492..73fe84fdef 100644
--- a/src/quick/items/qquickgridview.cpp
+++ b/src/quick/items/qquickgridview.cpp
@@ -208,6 +208,8 @@ public:
void updateHeader() override;
void updateFooter() override;
+ void initializeComponentItem(QQuickItem *item) const override;
+
void changedVisibleIndex(int newIndex) override;
void initializeCurrentItem() override;
@@ -349,9 +351,27 @@ qreal QQuickGridViewPrivate::rowPosAt(int modelIndex) const
return lastItem->rowPos() + rows * rowSize();
}
}
- return (modelIndex / columns) * rowSize();
-}
+ qreal rowPos = ((modelIndex / columns) * rowSize());
+
+ if (flow == QQuickGridView::FlowLeftToRight && verticalLayoutDirection == QQuickItemView::TopToBottom) {
+ // Add the effective startpos of row 0. Start by subtracting minExtent, which will contain the
+ // height of the rows outside the beginning of the content item. (Rows can end up outside if
+ // e.g flicking the viewport a long way down, changing cellSize, and then flick back).
+ // NOTE: It's not clearly understood why the flow == QQuickGridView::FlowLeftToRight guard is
+ // needed, since the flow shouldn't normally affect the y postition of an index. But without
+ // it, several auto tests start failing, so we keep it until this part is better understood.
+ rowPos -= minExtent;
+ // minExtent will also contain the size of the topMargin (vData.startMargin), the header, and
+ // the highlightRangeStart. Those should be added before the start of row 0. So we need to subtract
+ // them from the rowPos. But only the largest of topMargin and highlightRangeStart will need
+ // to be taken into account, since having a topMargin will also ensure that currentItem ends
+ // up within the requested highlight range when view is positioned at the beginning.
+ rowPos += qMax(vData.startMargin, highlightRangeStart) + headerSize();
+ }
+
+ return rowPos;
+}
qreal QQuickGridViewPrivate::snapPosAt(qreal pos) const
{
@@ -835,6 +855,14 @@ void QQuickGridViewPrivate::updateFooter()
emit q->footerItemChanged();
}
+void QQuickGridViewPrivate::initializeComponentItem(QQuickItem *item) const
+{
+ QQuickGridViewAttached *attached = static_cast<QQuickGridViewAttached *>(
+ qmlAttachedPropertiesObject<QQuickGridView>(item));
+ if (attached)
+ attached->setView(const_cast<QQuickGridView*>(q_func()));
+}
+
void QQuickGridViewPrivate::updateHeader()
{
Q_Q(QQuickGridView);
diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp
index 4226bb7a57..4426c26cab 100644
--- a/src/quick/items/qquickimage.cpp
+++ b/src/quick/items/qquickimage.cpp
@@ -494,6 +494,8 @@ qreal QQuickImage::paintedHeight() const
\note \e {Changing this property dynamically causes the image source to be reloaded,
potentially even from the network, if it is not in the disk cache.}
+
+ \sa {Pointer Handlers Example}
*/
/*!
@@ -970,3 +972,7 @@ void QQuickImage::setMipmap(bool use)
*/
QT_END_NAMESPACE
+
+#include "moc_qquickimage_p_p.cpp"
+
+#include "moc_qquickimage_p.cpp"
diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp
index 619b3c299b..596cd7d63a 100644
--- a/src/quick/items/qquickimagebase.cpp
+++ b/src/quick/items/qquickimagebase.cpp
@@ -51,6 +51,17 @@
QT_BEGIN_NAMESPACE
+bool isScalableImageFormat(const QUrl &url)
+{
+ if (url.scheme() == QLatin1String("image"))
+ return true;
+
+ const QString stringUrl = url.path(QUrl::PrettyDecoded);
+ return stringUrl.endsWith(QLatin1String("svg"))
+ || stringUrl.endsWith(QLatin1String("svgz"))
+ || stringUrl.endsWith(QLatin1String("pdf"));
+}
+
// 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)
@@ -58,17 +69,7 @@ bool QQuickImageBasePrivate::updateDevicePixelRatio(qreal targetDevicePixelRatio
// QQuickImageProvider and SVG and PDF 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 (url.scheme() == QLatin1String("image")) {
- setDevicePixelRatio = true;
- } else {
- QString stringUrl = url.path(QUrl::PrettyDecoded);
- if (stringUrl.endsWith(QLatin1String("svg")) ||
- stringUrl.endsWith(QLatin1String("svgz")) ||
- stringUrl.endsWith(QLatin1String("pdf"))) {
- setDevicePixelRatio = true;
- }
- }
+ const bool setDevicePixelRatio = isScalableImageFormat(url);
if (setDevicePixelRatio)
devicePixelRatio = targetDevicePixelRatio;
@@ -326,8 +327,10 @@ void QQuickImageBase::loadPixmap(const QUrl &url, LoadPixmapOptions loadOptions)
const qreal targetDevicePixelRatio = (window() ? window()->effectiveDevicePixelRatio() : qApp->devicePixelRatio());
d->devicePixelRatio = 1.0;
bool updatedDevicePixelRatio = false;
- if (d->sourcesize.isValid())
+ if (d->sourcesize.isValid()
+ || (isScalableImageFormat(d->url) && d->url.scheme() != QLatin1String("image"))) {
updatedDevicePixelRatio = d->updateDevicePixelRatio(targetDevicePixelRatio);
+ }
if (!updatedDevicePixelRatio) {
// (possible) local file: loadUrl and d->devicePixelRatio will be modified if
@@ -343,7 +346,8 @@ void QQuickImageBase::loadPixmap(const QUrl &url, LoadPixmapOptions loadOptions)
(loadOptions & HandleDPR) ? d->sourcesize * d->devicePixelRatio : QSize(),
options,
(loadOptions & UseProviderOptions) ? d->providerOptions : QQuickImageProviderOptions(),
- d->currentFrame, d->frameCount);
+ d->currentFrame, d->frameCount,
+ d->devicePixelRatio);
if (d->pix.isLoading()) {
if (d->progress != 0.0) {
diff --git a/src/quick/items/qquickimplicitsizeitem_p.h b/src/quick/items/qquickimplicitsizeitem_p.h
index 8ae8f9f447..41f682fe57 100644
--- a/src/quick/items/qquickimplicitsizeitem_p.h
+++ b/src/quick/items/qquickimplicitsizeitem_p.h
@@ -62,6 +62,8 @@ class Q_QUICK_PRIVATE_EXPORT QQuickImplicitSizeItem : public QQuickItem
Q_OBJECT
Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged)
Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged)
+ QML_ANONYMOUS
+ QML_ADDED_IN_VERSION(6, 2)
protected:
QQuickImplicitSizeItem(QQuickImplicitSizeItemPrivate &dd, QQuickItem *parent);
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index cdaf4773f1..dfa88c0a04 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -53,6 +53,7 @@
#include <QtGui/qpen.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qstylehints.h>
+#include <QtGui/private/qeventpoint_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qpointingdevice_p.h>
#include <QtGui/qinputmethod.h>
@@ -60,6 +61,7 @@
#include <QtCore/private/qnumeric_p.h>
#include <QtGui/qpa/qplatformtheme.h>
#include <QtCore/qloggingcategory.h>
+#include <QtCore/private/qduplicatetracker_p.h>
#include <private/qqmlglobal_p.h>
#include <private/qqmlengine_p.h>
@@ -403,42 +405,42 @@ void QQuickItemKeyFilter::componentComplete()
*/
/*!
- \qmlproperty Item QtQuick::KeyNavigation::left
+ \qmlattachedproperty Item QtQuick::KeyNavigation::left
This property holds the item to assign focus to
when the left cursor key is pressed.
*/
/*!
- \qmlproperty Item QtQuick::KeyNavigation::right
+ \qmlattachedproperty Item QtQuick::KeyNavigation::right
This property holds the item to assign focus to
when the right cursor key is pressed.
*/
/*!
- \qmlproperty Item QtQuick::KeyNavigation::up
+ \qmlattachedproperty Item QtQuick::KeyNavigation::up
This property holds the item to assign focus to
when the up cursor key is pressed.
*/
/*!
- \qmlproperty Item QtQuick::KeyNavigation::down
+ \qmlattachedproperty Item QtQuick::KeyNavigation::down
This property holds the item to assign focus to
when the down cursor key is pressed.
*/
/*!
- \qmlproperty Item QtQuick::KeyNavigation::tab
+ \qmlattachedproperty Item QtQuick::KeyNavigation::tab
This property holds the item to assign focus to
when the Tab key is pressed.
*/
/*!
- \qmlproperty Item QtQuick::KeyNavigation::backtab
+ \qmlattachedproperty Item QtQuick::KeyNavigation::backtab
This property holds the item to assign focus to
when the Shift+Tab key combination (Backtab) is pressed.
@@ -590,7 +592,7 @@ void QQuickKeyNavigationAttached::setBacktab(QQuickItem *i)
}
/*!
- \qmlproperty enumeration QtQuick::KeyNavigation::priority
+ \qmlattachedproperty enumeration QtQuick::KeyNavigation::priority
This property determines whether the keys are processed before
or after the attached item's own key handling.
@@ -908,7 +910,7 @@ bool QQuickKeysAttached::isConnected(const char *signalName) const
*/
/*!
- \qmlproperty list<Object> QtQuick::Keys::forwardTo
+ \qmlproperty list<Item> QtQuick::Keys::forwardTo
This property provides a way to forward key presses, key releases, and keyboard input
coming from input methods to other items. This can be useful when you want
@@ -2351,31 +2353,22 @@ QQuickItem::~QQuickItem()
while (!d->childItems.isEmpty())
d->childItems.constFirst()->setParentItem(nullptr);
- if (!d->changeListeners.isEmpty()) {
- const auto listeners = d->changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate();
- if (anchor)
- anchor->clearItem(this);
- }
-
- /*
+ d->notifyChangeListeners(QQuickItemPrivate::AllChanges, [this](const QQuickItemPrivate::ChangeListener &change){
+ QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate();
+ if (anchor)
+ anchor->clearItem(this);
+ });
+ /*
update item anchors that depended on us unless they are our child (and will also be destroyed),
or our sibling, and our parent is also being destroyed.
*/
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate();
- if (anchor && anchor->item && anchor->item->parentItem() && anchor->item->parentItem() != this)
- anchor->update();
- }
-
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::Destroyed)
- change.listener->itemDestroyed(this);
- }
-
- d->changeListeners.clear();
- }
+ d->notifyChangeListeners(QQuickItemPrivate::AllChanges, [this](const QQuickItemPrivate::ChangeListener &change){
+ QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate();
+ if (anchor && anchor->item && anchor->item->parentItem() && anchor->item->parentItem() != this)
+ anchor->update();
+ });
+ d->notifyChangeListeners(QQuickItemPrivate::Destroyed, &QQuickItemChangeListener::itemDestroyed, this);
+ d->changeListeners.clear();
/*
Remove any references our transforms have to us, in case they try to
@@ -2539,6 +2532,7 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo
QQuickItem *current = item;
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: startItem:" << startItem;
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: firstFromItem:" << firstFromItem;
+ QDuplicateTracker<QQuickItem *> cycleDetector;
do {
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: current:" << current;
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: from:" << from;
@@ -2605,7 +2599,10 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo
// traversed all of the chain (by compare the [current] item with [startItem])
// Since the [startItem] might be promoted to its parent if it is invisible,
// we still have to check [current] item with original start item
- if ((current == startItem || current == originalStartItem) && from == firstFromItem) {
+ // We might also run into a cycle before we reach firstFromItem again
+ // but note that we have to ignore current if we are meant to skip it
+ if (((current == startItem || current == originalStartItem) && from == firstFromItem) ||
+ (!skip && cycleDetector.hasSeen(current))) {
// wrapped around, avoid endless loops
if (item == contentItem) {
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: looped, return contentItem";
@@ -3658,14 +3655,7 @@ QQuickAnchors *QQuickItemPrivate::anchors() const
void QQuickItemPrivate::siblingOrderChanged()
{
Q_Q(QQuickItem);
- if (!changeListeners.isEmpty()) {
- const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::SiblingOrder) {
- change.listener->itemSiblingOrderChanged(q);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::SiblingOrder, &QQuickItemChangeListener::itemSiblingOrderChanged, q);
}
QQmlListProperty<QObject> QQuickItemPrivate::data()
@@ -3733,6 +3723,9 @@ QList<QQuickItem *> QQuickItem::childItems() const
If clipping is enabled, an item will clip its own painting, as well
as the painting of its children, to its bounding rectangle.
+
+ \note Clipping can affect rendering performance. See \l {Clipping} for more
+ information.
*/
/*!
\property QQuickItem::clip
@@ -3783,15 +3776,10 @@ void QQuickItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeom
change.setWidthChange(newGeometry.width() != oldGeometry.width());
change.setHeightChange(newGeometry.height() != oldGeometry.height());
- if (!d->changeListeners.isEmpty()) {
- const auto listeners = d->changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &listener : listeners) {
- if (listener.types & QQuickItemPrivate::Geometry) {
- if (change.matches(listener.gTypes))
- listener.listener->itemGeometryChanged(this, change, oldGeometry);
- }
- }
- }
+ d->notifyChangeListeners(QQuickItemPrivate::Geometry, [&](const QQuickItemPrivate::ChangeListener &listener){
+ if (change.matches(listener.gTypes))
+ listener.listener->itemGeometryChanged(this, change, oldGeometry);
+ });
// The notify method takes care of emitting the signal, and also notifies any
// property observers.
@@ -4419,16 +4407,11 @@ void QQuickItem::setBaselineOffset(qreal offset)
d->baselineOffset = offset;
- if (!d->changeListeners.isEmpty()) {
- const auto listeners = d->changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::Geometry) {
- QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate();
- if (anchor)
- anchor->updateVerticalAnchors();
- }
- }
- }
+ d->notifyChangeListeners(QQuickItemPrivate::Geometry, [](const QQuickItemPrivate::ChangeListener &change){
+ QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate();
+ if (anchor)
+ anchor->updateVerticalAnchors();
+ });
if (d->_anchors && (d->_anchors->usedAnchors() & QQuickAnchors::BaselineAnchor))
QQuickAnchorsPrivate::get(d->_anchors)->updateVerticalAnchors();
@@ -4564,10 +4547,10 @@ static bool unwrapMapFromToFromItemArgs(QQmlV4Function *args, const QQuickItem *
}
/*!
- \qmlmethod object QtQuick::Item::mapFromItem(Item item, real x, real y)
- \qmlmethod object QtQuick::Item::mapFromItem(Item item, point p)
- \qmlmethod object QtQuick::Item::mapFromItem(Item item, real x, real y, real width, real height)
- \qmlmethod object QtQuick::Item::mapFromItem(Item item, rect r)
+ \qmlmethod point QtQuick::Item::mapFromItem(Item item, real x, real y)
+ \qmlmethod point QtQuick::Item::mapFromItem(Item item, point p)
+ \qmlmethod rect QtQuick::Item::mapFromItem(Item item, real x, real y, real width, real height)
+ \qmlmethod rect QtQuick::Item::mapFromItem(Item item, rect r)
Maps the point (\a x, \a y) or rect (\a x, \a y, \a width, \a height), which is in \a
item's coordinate system, to this item's coordinate system, and returns a \l point or \l rect
@@ -4619,10 +4602,10 @@ QTransform QQuickItem::itemTransform(QQuickItem *other, bool *ok) const
}
/*!
- \qmlmethod object QtQuick::Item::mapToItem(Item item, real x, real y)
- \qmlmethod object QtQuick::Item::mapToItem(Item item, point p)
- \qmlmethod object QtQuick::Item::mapToItem(Item item, real x, real y, real width, real height)
- \qmlmethod object QtQuick::Item::mapToItem(Item item, rect r)
+ \qmlmethod point QtQuick::Item::mapToItem(Item item, real x, real y)
+ \qmlmethod point QtQuick::Item::mapToItem(Item item, point p)
+ \qmlmethod rect QtQuick::Item::mapToItem(Item item, real x, real y, real width, real height)
+ \qmlmethod rect QtQuick::Item::mapToItem(Item item, rect r)
Maps the point (\a x, \a y) or rect (\a x, \a y, \a width, \a height), which is in this
item's coordinate system, to \a item's coordinate system, and returns a \l point or \l rect
@@ -4704,7 +4687,7 @@ static bool unwrapMapFromToFromGlobalArgs(QQmlV4Function *args, const QQuickItem
/*!
\since 5.7
- \qmlmethod object QtQuick::Item::mapFromGlobal(real x, real y)
+ \qmlmethod point QtQuick::Item::mapFromGlobal(real x, real y)
Maps the point (\a x, \a y), which is in the global coordinate system, to the
item's coordinate system, and returns a \l point matching the mapped coordinate.
@@ -4731,7 +4714,7 @@ void QQuickItem::mapFromGlobal(QQmlV4Function *args) const
/*!
\since 5.7
- \qmlmethod object QtQuick::Item::mapToGlobal(real x, real y)
+ \qmlmethod point QtQuick::Item::mapToGlobal(real x, real y)
Maps the point (\a x, \a y), which is in this item's coordinate system, to the
global coordinate system, and returns a \l point matching the mapped coordinate.
@@ -6424,26 +6407,12 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
switch (change) {
case QQuickItem::ItemChildAddedChange: {
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::Children) {
- change.listener->itemChildAdded(q, data.item);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::Children, &QQuickItemChangeListener::itemChildAdded, q, data.item);
break;
}
case QQuickItem::ItemChildRemovedChange: {
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::Children) {
- change.listener->itemChildRemoved(q, data.item);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::Children, &QQuickItemChangeListener::itemChildRemoved, q, data.item);
break;
}
case QQuickItem::ItemSceneChange:
@@ -6451,50 +6420,22 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
break;
case QQuickItem::ItemVisibleHasChanged: {
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::Visibility) {
- change.listener->itemVisibilityChanged(q);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::Visibility, &QQuickItemChangeListener::itemVisibilityChanged, q);
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);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::Enabled, &QQuickItemChangeListener::itemEnabledChanged, q);
break;
}
case QQuickItem::ItemParentHasChanged: {
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::Parent) {
- change.listener->itemParentChanged(q, data.item);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::Parent, &QQuickItemChangeListener::itemParentChanged, q, data.item);
break;
}
case QQuickItem::ItemOpacityHasChanged: {
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::Opacity) {
- change.listener->itemOpacityChanged(q);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::Opacity, &QQuickItemChangeListener::itemOpacityChanged, q);
break;
}
case QQuickItem::ItemActiveFocusHasChanged:
@@ -6502,14 +6443,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
break;
case QQuickItem::ItemRotationHasChanged: {
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::Rotation) {
- change.listener->itemRotationChanged(q);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::Rotation, &QQuickItemChangeListener::itemRotationChanged, q);
break;
}
case QQuickItem::ItemAntialiasingHasChanged:
@@ -6787,16 +6721,17 @@ void QQuickItem::setX(qreal v)
d->x.removeBindingUnlessInWrapper();
if (qt_is_nan(v))
return;
- if (d->x == v)
+
+ const qreal oldx = d->x;
+ if (oldx == v)
return;
- qreal oldx = d->x;
d->x = v;
d->dirty(QQuickItemPrivate::Position);
- geometryChange(QRectF(v, d->y, d->width, d->height),
- QRectF(oldx, d->y, d->width, d->height));
+ const qreal y = d->y, w = d->width, h = d->height;
+ geometryChange(QRectF(v, y, w, h), QRectF(oldx, y, w, h));
}
void QQuickItem::setY(qreal v)
@@ -6805,18 +6740,19 @@ void QQuickItem::setY(qreal v)
d->y.removeBindingUnlessInWrapper();
if (qt_is_nan(v))
return;
- if (d->y == v)
+
+ const qreal oldy = d->y;
+ if (oldy == v)
return;
- qreal oldy = d->y;
d->y = v;
d->dirty(QQuickItemPrivate::Position);
// we use v instead of d->y, as that avoid a method call
// and we have v anyway in scope
- geometryChange(QRectF(d->x, v, d->width, d->height),
- QRectF(d->x, oldy, d->width, d->height));
+ const qreal x = d->x, w = d->width, h = d->height;
+ geometryChange(QRectF(x, v, w, h), QRectF(x, oldy, w, h));
}
/*!
@@ -6828,8 +6764,8 @@ void QQuickItem::setPosition(const QPointF &pos)
if (QPointF(d->x, d->y) == pos)
return;
- qreal oldx = d->x;
- qreal oldy = d->y;
+ const qreal oldx = d->x;
+ const qreal oldy = d->y;
/* This preserves the bindings, because that was what the code used to do
The effect of this is that you can have
@@ -6849,8 +6785,8 @@ void QQuickItem::setPosition(const QPointF &pos)
d->dirty(QQuickItemPrivate::Position);
- geometryChange(QRectF(d->x, d->y, d->width, d->height),
- QRectF(oldx, oldy, d->width, d->height));
+ const qreal w = d->width, h = d->height;
+ geometryChange(QRectF(pos.x(), pos.y(), w, h), QRectF(oldx, oldy, w, h));
}
/* The bindable methods return an object which supports inspection (hasBinding) and
@@ -6885,16 +6821,16 @@ void QQuickItem::setWidth(qreal w)
return;
d->widthValidFlag = true;
- if (d->width == w)
+ const qreal oldWidth = d->width;
+ if (oldWidth == w)
return;
- qreal oldWidth = d->width;
d->width = w;
d->dirty(QQuickItemPrivate::Size);
- geometryChange(QRectF(d->x, d->y, w, d->height),
- QRectF(d->x, d->y, oldWidth, d->height));
+ const qreal x = d->x, y = d->y, h = d->height;
+ geometryChange(QRectF(x, y, w, h), QRectF(x, y, oldWidth, h));
}
void QQuickItem::resetWidth()
@@ -6908,14 +6844,7 @@ void QQuickItem::resetWidth()
void QQuickItemPrivate::implicitWidthChanged()
{
Q_Q(QQuickItem);
- if (!changeListeners.isEmpty()) {
- const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::ImplicitWidth) {
- change.listener->itemImplicitWidthChanged(q);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::ImplicitWidth, &QQuickItemChangeListener::itemImplicitWidthChanged, q);
emit q->implicitWidthChanged();
}
@@ -6941,7 +6870,14 @@ QBindable<qreal> QQuickItem::bindableWidth()
\qmlproperty real QtQuick::Item::implicitWidth
\qmlproperty real QtQuick::Item::implicitHeight
- Defines the natural width or height of the Item if no \l width or \l height is specified.
+ Defines the preferred width or height of the Item.
+
+ If \l width or \l height is not specified, an item's effective size will be
+ determined by its \l implicitWidth or \l implicitHeight.
+
+ However, if an item is the child of a \l {Qt Quick Layouts}{layout}, the
+ layout will determine the item's preferred size using its implicit size.
+ In such a scenario, the explicit \l width or \l height will be ignored.
The default implicit size for most items is 0x0, however some items have an inherent
implicit size which cannot be overridden, for example, \l [QML] Image and \l [QML] Text.
@@ -6975,7 +6911,14 @@ QBindable<qreal> QQuickItem::bindableWidth()
\property QQuickItem::implicitWidth
\property QQuickItem::implicitHeight
- Defines the natural width or height of the Item if no \l width or \l height is specified.
+ Defines the preferred width or height of the Item.
+
+ If \l width or \l height is not specified, an item's effective size will be
+ determined by its \l implicitWidth or \l implicitHeight.
+
+ However, if an item is the child of a \l {Qt Quick Layouts}{layout}, the
+ layout will determine the item's preferred size using its implicit size.
+ In such a scenario, the explicit \l width or \l height will be ignored.
The default implicit size for most items is 0x0, however some items have an inherent
implicit size which cannot be overridden, for example, \l [QML] Image and \l [QML] Text.
@@ -7019,19 +6962,18 @@ void QQuickItem::setImplicitWidth(qreal w)
changed = false;
}
- qreal oldWidth = d->width.valueBypassingBindings();
+ const qreal oldWidth = d->width.valueBypassingBindings();
Q_ASSERT(!d->width.hasBinding() || QQmlPropertyBinding::isUndefined(d->width.binding()));
// we need to keep the binding if its undefined (therefore we can't use operator=/setValue)
d->width.setValueBypassingBindings(w);
d->dirty(QQuickItemPrivate::Size);
- qreal x = d->x.valueBypassingBindings();
- qreal y = d->y.valueBypassingBindings();
- qreal width = w;
- qreal height = d->height.valueBypassingBindings();
- geometryChange(QRectF(x, y, width, height),
- QRectF(x, y, oldWidth, height));
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal y = d->y.valueBypassingBindings();
+ const qreal width = w;
+ const qreal height = d->height.valueBypassingBindings();
+ geometryChange(QRectF(x, y, width, height), QRectF(x, y, oldWidth, height));
if (changed)
d->implicitWidthChanged();
@@ -7085,16 +7027,16 @@ void QQuickItem::setHeight(qreal h)
return;
d->heightValidFlag = true;
- if (d->height == h)
+ const qreal oldHeight = d->height;
+ if (oldHeight == h)
return;
- qreal oldHeight = d->height;
d->height = h;
d->dirty(QQuickItemPrivate::Size);
- geometryChange(QRectF(d->x, d->y, d->width, h),
- QRectF(d->x, d->y, d->width, oldHeight));
+ const qreal x = d->x, y = d->y, w = d->width;
+ geometryChange(QRectF(x, y, w, h), QRectF(x, y, w, oldHeight));
}
void QQuickItem::resetHeight()
@@ -7111,14 +7053,7 @@ void QQuickItem::resetHeight()
void QQuickItemPrivate::implicitHeightChanged()
{
Q_Q(QQuickItem);
- if (!changeListeners.isEmpty()) {
- const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::ImplicitHeight) {
- change.listener->itemImplicitHeightChanged(q);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::ImplicitHeight, &QQuickItemChangeListener::itemImplicitHeightChanged, q);
emit q->implicitHeightChanged();
}
@@ -7151,17 +7086,17 @@ void QQuickItem::setImplicitHeight(qreal h)
changed = false;
}
- qreal oldHeight = d->height.valueBypassingBindings();
+ const qreal oldHeight = d->height.valueBypassingBindings();
Q_ASSERT(!d->height.hasBinding() || QQmlPropertyBinding::isUndefined(d->height.binding()));
// we need to keep the binding if its undefined (therefore we can't use operator=/setValue)
d->height.setValueBypassingBindings(h);
d->dirty(QQuickItemPrivate::Size);
- qreal x = d->x.valueBypassingBindings();
- qreal y = d->y.valueBypassingBindings();
- qreal width = d->width.valueBypassingBindings();
- qreal height = d->height.valueBypassingBindings();
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal y = d->y.valueBypassingBindings();
+ const qreal width = d->width.valueBypassingBindings();
+ const qreal height = d->height.valueBypassingBindings();
geometryChange(QRectF(x, y, width, height),
QRectF(x, y, width, oldHeight));
@@ -7200,8 +7135,8 @@ void QQuickItem::setImplicitSize(qreal w, qreal h)
if (wDone && hDone)
return;
- qreal oldWidth = width;
- qreal oldHeight = height;
+ const qreal oldWidth = width;
+ const qreal oldHeight = height;
if (!wDone) {
width = w;
d->width = w;
@@ -7213,8 +7148,8 @@ void QQuickItem::setImplicitSize(qreal w, qreal h)
d->dirty(QQuickItemPrivate::Size);
- qreal x = d->x.valueBypassingBindings();
- qreal y = d->y.valueBypassingBindings();
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal y = d->y.valueBypassingBindings();
geometryChange(QRectF(x, y, width, height),
QRectF(x, y, oldWidth, oldHeight));
@@ -7267,15 +7202,15 @@ void QQuickItem::setSize(const QSizeF &size)
if (d->width == size.width() && d->height == size.height())
return;
- qreal oldHeight = d->height;
- qreal oldWidth = d->width;
+ const qreal oldHeight = d->height;
+ const qreal oldWidth = d->width;
d->height.setValueBypassingBindings(size.height());
d->width.setValueBypassingBindings(size.width());
d->dirty(QQuickItemPrivate::Size);
- geometryChange(QRectF(d->x, d->y, d->width, d->height),
- QRectF(d->x, d->y, oldWidth, oldHeight));
+ const qreal x = d->x, y = d->y;
+ geometryChange(QRectF(x, y, size.width(), size.height()), QRectF(x, y, oldWidth, oldHeight));
}
/*!
@@ -7456,20 +7391,19 @@ void QQuickItem::setFocus(bool focus, Qt::FocusReason reason)
if (d->focus == focus)
return;
+ bool notifyListeners = false;
if (d->window || d->parentItem) {
// Need to find our nearest focus scope
QQuickItem *scope = parentItem();
while (scope && !scope->isFocusScope() && scope->parentItem())
scope = scope->parentItem();
if (d->window) {
- if (reason != Qt::PopupFocusReason) {
- auto da = d->deliveryAgentPrivate();
- Q_ASSERT(da);
- if (focus)
- da->setFocusInScope(scope, this, reason);
- else
- da->clearFocusInScope(scope, this, reason);
- }
+ auto da = d->deliveryAgentPrivate();
+ Q_ASSERT(da);
+ if (focus)
+ da->setFocusInScope(scope, this, reason);
+ else
+ da->clearFocusInScope(scope, this, reason);
} else {
// do the focus changes from setFocusInScope/clearFocusInScope that are
// unrelated to a window
@@ -7487,9 +7421,10 @@ void QQuickItem::setFocus(bool focus, Qt::FocusReason reason)
d->focus = focus;
changed << this;
+ notifyListeners = true;
emit focusChanged(focus);
- QQuickDeliveryAgentPrivate::notifyFocusChangesRecur(changed.data(), changed.count() - 1);
+ QQuickDeliveryAgentPrivate::notifyFocusChangesRecur(changed.data(), changed.count() - 1, reason);
}
} else {
QVarLengthArray<QQuickItem *, 20> changed;
@@ -7502,10 +7437,13 @@ void QQuickItem::setFocus(bool focus, Qt::FocusReason reason)
d->focus = focus;
changed << this;
+ notifyListeners = true;
emit focusChanged(focus);
- QQuickDeliveryAgentPrivate::notifyFocusChangesRecur(changed.data(), changed.count() - 1);
+ QQuickDeliveryAgentPrivate::notifyFocusChangesRecur(changed.data(), changed.count() - 1, reason);
}
+ if (notifyListeners)
+ d->notifyChangeListeners(QQuickItemPrivate::Focus, &QQuickItemChangeListener::itemFocusChanged, this, reason);
}
/*!
@@ -7635,7 +7573,7 @@ bool QQuickItem::isUnderMouse() const
return false;
QPointF cursorPos = QGuiApplicationPrivate::lastCursorPosition;
- return contains(mapFromScene(d->window->mapFromGlobal(cursorPos.toPoint())));
+ return contains(mapFromScene(d->window->mapFromGlobal(cursorPos)));
}
/*!
@@ -7802,7 +7740,7 @@ void QQuickItem::setCursor(const QCursor &cursor)
if (d->window) {
QWindow *renderWindow = QQuickRenderControl::renderWindowFor(d->window);
QWindow *window = renderWindow ? renderWindow : d->window;
- QPointF pos = window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
+ QPointF pos = window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition);
if (contains(mapFromScene(pos)))
updateCursorPos = pos;
}
@@ -7831,7 +7769,7 @@ void QQuickItem::unsetCursor()
if (d->window) {
QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(d->window);
if (windowPrivate->cursorItem == this) {
- QPointF pos = d->window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
+ QPointF pos = d->window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition);
windowPrivate->updateCursor(pos);
}
}
@@ -8065,7 +8003,7 @@ void QQuickItem::setKeepTouchGrab(bool keep)
Returns \c true if this item contains \a point, which is in local coordinates;
returns \c false otherwise. This is the same check that is used for
hit-testing a QEventPoint during event delivery, and is affected by
- containmentMask() if it is set.
+ \l containmentMask if it is set.
*/
/*!
Returns \c true if this item contains \a point, which is in local coordinates;
@@ -8073,7 +8011,7 @@ void QQuickItem::setKeepTouchGrab(bool keep)
This function can be overridden in order to handle point collisions in items
with custom shapes. The default implementation checks whether the point is inside
- containmentMask() if it is set, or inside the bounding box otherwise.
+ \l containmentMask() if it is set, or inside the bounding box otherwise.
\note This method is used for hit-testing each QEventPoint during event
delivery, so the implementation should be kept as lightweight as possible.
@@ -8102,10 +8040,10 @@ bool QQuickItem::contains(const QPointF &point) const
\qmlproperty QObject* QtQuick::Item::containmentMask
\since 5.11
This property holds an optional mask for the Item to be used in the
- QtQuick::Item::contains() method. Its main use is currently to determine
+ \l contains() method. Its main use is currently to determine
whether a \l {QPointerEvent}{pointer event} has landed into the item or not.
- By default the \l contains method will return true for any point
+ By default the \c contains() method will return true for any point
within the Item's bounding box. \c containmentMask allows for
more fine-grained control. For example, if a custom C++
QQuickItem subclass with a specialized contains() method
@@ -8670,7 +8608,7 @@ QSGTextureProvider *QQuickItem::textureProvider() const
}
\endcode
- \sa Window::palette, Popup::palette, ColorGroup, Palette
+ \sa Window::palette, Popup::palette, ColorGroup, Palette, SystemPalette
*/
/*!
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index 6a3a5db921..fcaedfb50d 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -472,6 +472,7 @@ private:
friend class QSGRenderer;
friend class QAccessibleQuickItem;
friend class QQuickAccessibleAttached;
+ friend class QQuickAnchorChanges;
Q_DISABLE_COPY(QQuickItem)
Q_DECLARE_PRIVATE(QQuickItem)
};
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index d8a7c1dfdc..4406e191cb 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -331,6 +331,8 @@ public:
ImplicitWidth = 0x100,
ImplicitHeight = 0x200,
Enabled = 0x400,
+ Focus = 0x800,
+ AllChanges = 0xFFFFFFFF
};
Q_DECLARE_FLAGS(ChangeTypes, ChangeType)
@@ -358,6 +360,32 @@ public:
QQuickGeometryChange gTypes; //NOTE: not used for ==
};
+ // call QQuickItemChangeListener PMF
+ template <typename Fn, typename ...Args>
+ void notifyChangeListeners(QQuickItemPrivate::ChangeTypes changeTypes, Fn &&function, Args &&...args)
+ {
+ if (changeListeners.isEmpty())
+ return;
+
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
+ for (const QQuickItemPrivate::ChangeListener &change : listeners) {
+ if (change.types & changeTypes)
+ (change.listener->*function)(args...);
+ }
+ }
+ // call functor
+ template <typename Fn>
+ void notifyChangeListeners(QQuickItemPrivate::ChangeTypes changeTypes, Fn &&function) {
+ if (changeListeners.isEmpty())
+ return;
+
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
+ for (const QQuickItemPrivate::ChangeListener &change : listeners) {
+ if (change.types & changeTypes)
+ function(change);
+ }
+ }
+
struct ExtraData {
ExtraData();
diff --git a/src/quick/items/qquickitemchangelistener_p.h b/src/quick/items/qquickitemchangelistener_p.h
index 31d06c9983..91e2d69cc0 100644
--- a/src/quick/items/qquickitemchangelistener_p.h
+++ b/src/quick/items/qquickitemchangelistener_p.h
@@ -134,6 +134,7 @@ public:
virtual void itemRotationChanged(QQuickItem *) {}
virtual void itemImplicitWidthChanged(QQuickItem *) {}
virtual void itemImplicitHeightChanged(QQuickItem *) {}
+ virtual void itemFocusChanged(QQuickItem *, Qt::FocusReason /* reason */) {}
virtual QQuickAnchorsPrivate *anchorPrivate() { return nullptr; }
};
diff --git a/src/quick/items/qquickitemgrabresult.h b/src/quick/items/qquickitemgrabresult.h
index a055c0638b..e800c71377 100644
--- a/src/quick/items/qquickitemgrabresult.h
+++ b/src/quick/items/qquickitemgrabresult.h
@@ -75,7 +75,7 @@ public:
#endif
#endif
Q_INVOKABLE bool saveToFile(const QString &fileName) const;
- Q_INVOKABLE bool saveToFile(const QUrl &fileName) const;
+ Q_REVISION(6, 2) Q_INVOKABLE bool saveToFile(const QUrl &fileName) const;
protected:
bool event(QEvent *) override;
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index 5201f6fd72..fbffde1c48 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -125,6 +125,8 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcTransient)
QT_END_NAMESPACE
+#include "moc_qquickitemsmodule_p.cpp"
+
static QQmlPrivate::AutoParentResult qquickitem_autoParent(QObject *obj, QObject *parent)
{
// When setting a parent (especially during dynamic object creation) in QML,
@@ -194,4 +196,138 @@ void QQuickItemsModule::defineModule()
qt_quickitems_defineModule();
}
+/*!
+ \qmltype PointerEvent
+ \instantiates QPointerEvent
+ \inqmlmodule QtQuick
+ \brief QML equivalent for \l QPointerEvent.
+
+ PointerEvent is the QML name of the QPointerEvent class.
+*/
+
+/*!
+ \qmltype PointerDevice
+ \instantiates QPointingDevice
+ \inqmlmodule QtQuick
+ \brief QML equivalent for \l QPointingDevice.
+
+ PointerDevice is the QML name of the QPointingDevice class.
+ It has the same properties and enums as \l QPointingDevice.
+*/
+
+/*!
+ \qmlproperty enumeration PointerDevice::deviceType
+
+ This property tells the type of device that generated a PointerEvent.
+
+ Valid values are:
+
+ \value PointerDevice.Unknown The device cannot be identified.
+ \value PointerDevice.Mouse A mouse.
+ \value PointerDevice.TouchScreen A touchscreen.
+ \value PointerDevice.TouchPad A touchpad or trackpad.
+ \value PointerDevice.Stylus A stylus on a graphics tablet.
+ \value PointerDevice.Airbrush An airbrush on a graphics tablet.
+ \value PointerDevice.Puck A digitizer with crosshairs, on a graphics tablet.
+
+ \sa QInputDevice::DeviceType, PointerDeviceHandler::acceptedDevices
+*/
+
+/*!
+ \qmlproperty enumeration PointerDevice::pointerType
+
+ This property tells what is interacting with the PointerDevice.
+
+ There is some redundancy between this property and \l deviceType.
+ For example, if a touchscreen is used, then \c deviceType is
+ \c TouchScreen and \c pointerType is \c Finger. But on a graphics
+ tablet, it's often possible for both ends of the stylus to be used,
+ and programs need to distinguish them.
+ \l PointerDeviceHandler::acceptedDevices and
+ \l PointerDeviceHandler::acceptedPointerTypes can be used in combination
+ to filter the subset of events that a particular handler should react to.
+
+ Valid values are:
+
+ \value PointerDevice.Unknown The device cannot be identified.
+ \value PointerDevice.Generic A mouse or a device that emulates a mouse.
+ \value PointerDevice.Finger A finger on a touchscreen.
+ \value PointerDevice.Pen A stylus on a graphics tablet.
+ \value PointerDevice.Eraser An eraser on a graphics tablet.
+ \value PointerDevice.Cursor A digitizer with crosshairs, on a graphics tablet.
+
+ \sa QPointingDevice::PointerType, PointerDeviceHandler::acceptedPointerTypes
+*/
+
+/*!
+ \qmlproperty int PointerDevice::maximumPoints
+
+ This property tells the maximum number of simultaneous touch points
+ (fingers) that can be detected.
+*/
+
+/*!
+ \qmlproperty int PointerDevice::buttonCount
+
+ This property tells the maximum number of on-device buttons that can be
+ detected.
+*/
+
+/*!
+ \qmltype pointingDeviceUniqueId
+ \instantiates QPointingDeviceUniqueId
+ \inqmlmodule QtQuick
+ \brief QML equivalent for \l QPointingDeviceUniqueId.
+
+ pointingDeviceUniqueId is the QML name of the QPointingDeviceUniqueId class.
+*/
+
+/*!
+ \qmlproperty qint64 pointingDeviceUniqueId::numericId
+
+ This property gives the numeric ID of the \l PointerDevice, if available;
+ otherwise it is \c -1.
+*/
+
+/*!
+ \qmlproperty pointingDeviceUniqueId PointerDevice::uniqueId
+
+ This property may provide a unique ID for the device, if available. For
+ example, a graphics tablet stylus device may have a unique serial number.
+
+ \sa eventPoint, QEventPoint::uniqueId()
+*/
+
+/*!
+ \qmlsignal PointerDevice::grabChanged(QtObject grabber, enumeration transition, PointerEvent event, eventPoint point)
+
+ This signal is emitted when the \a grabber object gains or loses an
+ exclusive or passive grab of \a point during delivery of \a event.
+ The \a transition tells what happened, from the perspective of the
+ \c grabber object, which may be either an \l Item or an
+ \l {Qt Quick Input Handlers}{Input Handler}.
+
+ Valid values for \a transition are:
+
+ \value GrabExclusive
+ The \a grabber has taken primary responsibility for handling the \a point.
+ \value UngrabExclusive
+ The \a grabber has given up its previous exclusive grab.
+ \value CancelGrabExclusive
+ The exclusive grab of \a grabber has been taken over or cancelled.
+ \value GrabPassive
+ The \a grabber has acquired a passive grab, to monitor the \a point.
+ \value UngrabPassive
+ The \a grabber has given up its previous passive grab.
+ \value CancelGrabPassive
+ The previous passive grab has terminated abnormally.
+
+ \note A grab transition from one object to another results in two signals,
+ to notify that one object has lost its grab, and to notify that there is
+ another grabber. In other cases, when transitioning to or from a non-grabbing
+ state, only one signal is emitted.
+
+ \sa QPointerEvent::setExclusiveGrabber(), QPointerEvent::addPassiveGrabber(), QPointerEvent::removePassiveGrabber()
+*/
+
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index f1336a91f6..faa03fa9ae 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -57,8 +57,6 @@ FxViewItem::FxViewItem(QQuickItem *i, QQuickItemView *v, bool own, QQuickItemVie
, view(v)
, attached(attached)
{
- if (attached) // can be null for default components (see createComponentItem)
- attached->setView(view);
}
QQuickItemViewChangeSet::QQuickItemViewChangeSet()
@@ -290,8 +288,6 @@ void QQuickItemView::setDelegate(QQmlComponent *delegate)
if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel*>(d->model)) {
int oldCount = dataModel->count();
dataModel->setDelegate(delegate);
- if (isComponentComplete())
- d->applyDelegateChange();
if (oldCount != dataModel->count())
emit countChanged();
}
@@ -1785,7 +1781,7 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to)
do {
bufferPause.stop();
- if (currentChanges.hasPendingChanges() || bufferedChanges.hasPendingChanges()) {
+ if (currentChanges.hasPendingChanges() || bufferedChanges.hasPendingChanges() || currentChanges.active) {
currentChanges.reset();
bufferedChanges.reset();
releaseVisibleItems(reusableFlag);
@@ -1972,6 +1968,9 @@ void QQuickItemViewPrivate::layout()
transitioner->resetTargetLists();
}
+ if (!currentItem)
+ updateCurrent(currentIndex);
+
runDelayedRemoveTransition = false;
inLayout = false;
}
@@ -2504,12 +2503,29 @@ QQuickItem *QQuickItemViewPrivate::createComponentItem(QQmlComponent *component,
item->setZ(zValue);
QQml_setParent_noEvent(item, q->contentItem());
item->setParentItem(q->contentItem());
+
+ initializeComponentItem(item);
}
if (component)
component->completeCreate();
return item;
}
+/*!
+ \internal
+
+ Allows derived classes to do any initialization required for \a item
+ before completeCreate() is called on it. For example, any attached
+ properties required by the item can be set.
+
+ This is similar to initItem(), but as that has logic specific to
+ delegate items, we use a separate function for non-delegates.
+*/
+void QQuickItemViewPrivate::initializeComponentItem(QQuickItem *item) const
+{
+ Q_UNUSED(item);
+}
+
void QQuickItemViewPrivate::updateTrackedItem()
{
Q_Q(QQuickItemView);
diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h
index 0bc2a6b768..36c36f43e8 100644
--- a/src/quick/items/qquickitemview_p_p.h
+++ b/src/quick/items/qquickitemview_p_p.h
@@ -76,7 +76,7 @@ public:
};
-class QQuickItemViewChangeSet
+class Q_AUTOTEST_EXPORT QQuickItemViewChangeSet
{
public:
QQuickItemViewChangeSet();
@@ -100,7 +100,7 @@ public:
};
-class Q_AUTOTEST_EXPORT QQuickItemViewPrivate : public QQuickFlickablePrivate, public QQuickItemViewTransitionChangeListener, public QAnimationJobChangeListener
+class Q_QUICK_AUTOTEST_EXPORT QQuickItemViewPrivate : public QQuickFlickablePrivate, public QQuickItemViewTransitionChangeListener, public QAnimationJobChangeListener
{
Q_DECLARE_PUBLIC(QQuickItemView)
public:
@@ -178,6 +178,7 @@ public:
QQuickItem *createHighlightItem() const;
QQuickItem *createComponentItem(QQmlComponent *component, qreal zValue, bool createDefault = false) const;
+ virtual void initializeComponentItem(QQuickItem *) const;
void updateCurrent(int modelIndex);
void updateTrackedItem();
diff --git a/src/quick/items/qquickitemviewtransition.cpp b/src/quick/items/qquickitemviewtransition.cpp
index b7649c9952..6b03d6c16b 100644
--- a/src/quick/items/qquickitemviewtransition.cpp
+++ b/src/quick/items/qquickitemviewtransition.cpp
@@ -500,6 +500,37 @@ void QQuickItemViewTransitionableItem::startTransition(QQuickItemViewTransitione
clearCurrentScheduledTransition();
}
+void QQuickItemViewTransitionableItem::completeTransition(QQuickTransition *quickTransition)
+{
+ if (nextTransitionType == QQuickItemViewTransitioner::NoTransition)
+ return;
+
+ if (!prepared) {
+ qWarning("QQuickViewItem::prepareTransition() not called!");
+ return;
+ }
+
+ if (!item) {
+ qWarning("No target for transition!");
+ return;
+ }
+
+ if (!transition || transition->m_type != nextTransitionType || transition->m_isTarget != isTransitionTarget) {
+ if (transition)
+ RETURN_IF_DELETED(transition->cancel());
+ delete transition;
+ transition = new QQuickItemViewTransitionJob;
+ }
+
+ QQuickStateOperation::ActionList actions; // not used
+ QList<QQmlProperty> after; // not used
+ QScopedPointer<QQuickTransitionInstance> instance(
+ quickTransition->prepare(actions, after, transition, item));
+ RETURN_IF_DELETED(instance->complete());
+
+ clearCurrentScheduledTransition();
+}
+
void QQuickItemViewTransitionableItem::setNextTransition(QQuickItemViewTransitioner::TransitionType type, bool isTargetItem)
{
// Don't reset nextTransitionToSet - once it is set, it cannot be changed
@@ -549,13 +580,15 @@ void QQuickItemViewTransitionableItem::stopTransition()
{
if (transition)
RETURN_IF_DELETED(transition->cancel());
+ delete transition;
+ transition = nullptr;
clearCurrentScheduledTransition();
resetNextTransitionPos();
}
QQuickViewTransitionAttached::QQuickViewTransitionAttached(QObject *parent)
- : QObject(parent), m_item(nullptr), m_index(-1)
+ : QObject(parent), m_index(-1)
{
}
/*!
diff --git a/src/quick/items/qquickitemviewtransition_p.h b/src/quick/items/qquickitemviewtransition_p.h
index 43858db688..253f3c6c70 100644
--- a/src/quick/items/qquickitemviewtransition_p.h
+++ b/src/quick/items/qquickitemviewtransition_p.h
@@ -157,6 +157,7 @@ public:
bool prepareTransition(QQuickItemViewTransitioner *transitioner, int index, const QRectF &viewBounds);
void startTransition(QQuickItemViewTransitioner *transitioner, int index);
+ void completeTransition(QQuickTransition *quickTransition);
SelfDeletable m_selfDeletable;
QPointF nextTransitionTo;
@@ -225,7 +226,7 @@ private:
QList<int> m_targetIndexes;
QList<QObject *> m_targetItems;
- QQuickItem *m_item;
+ QPointer<QQuickItem> m_item;
int m_index;
};
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index b954f1b107..58f031a9eb 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -130,6 +130,8 @@ public:
bool hasStickyHeader() const override;
bool hasStickyFooter() const override;
+ void initializeComponentItem(QQuickItem *item) const override;
+
void changedVisibleIndex(int newIndex) override;
void initializeCurrentItem() override;
@@ -1573,6 +1575,14 @@ bool QQuickListViewPrivate::hasStickyFooter() const
return footer && footerPositioning != QQuickListView::InlineFooter;
}
+void QQuickListViewPrivate::initializeComponentItem(QQuickItem *item) const
+{
+ QQuickListViewAttached *attached = static_cast<QQuickListViewAttached *>(
+ qmlAttachedPropertiesObject<QQuickListView>(item));
+ if (attached) // can be null for default components (see createComponentItem)
+ attached->setView(const_cast<QQuickListView*>(q_func()));
+}
+
void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change,
const QRectF &oldGeometry)
{
@@ -1642,6 +1652,10 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
return;
}
+ // update footer if all visible items have been removed
+ if (visibleItems.count() == 0)
+ updateFooter();
+
correctFlick = false;
fixupMode = moveReason == Mouse ? fixupMode : Immediate;
bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
@@ -2727,7 +2741,8 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
\c section.delegate holds the delegate component for each section. The
default \l {QQuickItem::z}{stacking order} of section delegate instances
- is \c 2.
+ is \c 2. If you declare a \c required property named "section" in it,
+ that property will contain the section's title.
\c section.labelPositioning determines whether the current and/or
next section labels stick to the start/end of the view, and whether
@@ -3743,8 +3758,10 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
item = createItem(it.index, QQmlIncubator::Synchronous);
if (!item)
return false;
- if (it.removedAtIndex)
+ if (it.removedAtIndex) {
+ releaseItem(item, reusableFlag);
continue;
+ }
visibleItems.insert(index, item);
if (index == 0)
diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp
index 4cd93ca1f0..1f5c55cffe 100644
--- a/src/quick/items/qquickloader.cpp
+++ b/src/quick/items/qquickloader.cpp
@@ -736,6 +736,9 @@ void QQuickLoaderPrivate::_q_sourceLoaded()
return;
}
+ if (!active)
+ return;
+
QQmlContext *creationContext = component->creationContext();
if (!creationContext) creationContext = qmlContext(q);
itemContext = new QQmlContext(creationContext);
@@ -801,7 +804,7 @@ void QQuickLoader::componentComplete()
{
Q_D(QQuickLoader);
QQuickItem::componentComplete();
- if (active()) {
+ if (active() && (status() != Ready)) {
if (d->loadingFromSource)
d->createComponent();
d->load();
@@ -939,7 +942,7 @@ void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
}
/*!
- \qmlproperty object QtQuick::Loader::item
+ \qmlproperty QtObject QtQuick::Loader::item
This property holds the top-level object that is currently loaded.
Since \c {QtQuick 2.0}, Loader can load any object type.
@@ -1038,9 +1041,15 @@ void QQuickLoaderPrivate::createComponent()
const QQmlComponent::CompilationMode mode = asynchronous
? QQmlComponent::Asynchronous
: QQmlComponent::PreferSynchronous;
- QQmlContext *context = qmlContext(q);
- component.setObject(new QQmlComponent(
- context->engine(), context->resolvedUrl(source), mode, q), q);
+ if (QQmlContext *context = qmlContext(q)) {
+ if (QQmlEngine *engine = context->engine()) {
+ component.setObject(new QQmlComponent(
+ engine, context->resolvedUrl(source), mode, q), q);
+ return;
+ }
+ }
+
+ qmlWarning(q) << "createComponent: Cannot find a QML engine.";
}
#include <moc_qquickloader_p.cpp>
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp
index 281860fa6e..4977ddf44f 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -808,7 +808,8 @@ void QQuickMouseArea::mouseReleaseEvent(QMouseEvent *event)
QQuickWindow *w = window();
if (w && w->mouseGrabberItem() == this)
ungrabMouse();
- setKeepMouseGrab(false);
+ if (!d->preventStealing)
+ setKeepMouseGrab(false);
}
}
d->doubleClick = false;
@@ -827,7 +828,8 @@ void QQuickMouseArea::mouseDoubleClickEvent(QMouseEvent *event)
emit this->doubleClicked(&me);
if (!me.isAccepted())
d->propagate(&me, QQuickMouseAreaPrivate::DoubleClick);
- d->doubleClick = d->isDoubleClickConnected() || me.isAccepted();
+ if (d->pressed)
+ d->doubleClick = d->isDoubleClickConnected() || me.isAccepted();
}
QQuickItem::mouseDoubleClickEvent(event);
}
@@ -849,14 +851,8 @@ void QQuickMouseArea::hoverEnterEvent(QHoverEvent *event)
me.setPosition(d->lastPos);
}
- if (auto parentMouseArea = qobject_cast<QQuickMouseArea *>(parentItem())) {
- if (parentMouseArea->acceptHoverEvents()) {
- // Special legacy case: if our parent is another MouseArea, and we're
- // hovered, the parent MouseArea should be hovered too. We achieve this
- // by simply ignoring the event to not block propagation.
- event->ignore();
- }
- }
+ // A MouseArea should not block hover events
+ event->ignore();
}
void QQuickMouseArea::hoverMoveEvent(QHoverEvent *event)
@@ -876,14 +872,8 @@ void QQuickMouseArea::hoverMoveEvent(QHoverEvent *event)
emit positionChanged(&me);
}
- if (auto parentMouseArea = qobject_cast<QQuickMouseArea *>(parentItem())) {
- if (parentMouseArea->acceptHoverEvents()) {
- // Special legacy case: if our parent is another MouseArea, and we're
- // hovered, the parent MouseArea should be hovered too. We achieve this
- // by simply ignoring the event to not block propagation.
- event->ignore();
- }
- }
+ // A MouseArea should not block hover events
+ event->ignore();
}
void QQuickMouseArea::hoverLeaveEvent(QHoverEvent *event)
@@ -894,14 +884,8 @@ void QQuickMouseArea::hoverLeaveEvent(QHoverEvent *event)
else
setHovered(false);
- if (auto parentMouseArea = qobject_cast<QQuickMouseArea *>(parentItem())) {
- if (parentMouseArea->acceptHoverEvents()) {
- // Special legacy case: if our parent is another MouseArea, and we're
- // hovered, the parent MouseArea should be hovered too. We achieve this
- // by simply ignoring the event to not block propagation.
- event->ignore();
- }
- }
+ // A MouseArea should not block hover events
+ event->ignore();
}
#if QT_CONFIG(wheelevent)
diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp
index 642b5ca047..8687de8f43 100644
--- a/src/quick/items/qquickmultipointtoucharea.cpp
+++ b/src/quick/items/qquickmultipointtoucharea.cpp
@@ -40,11 +40,11 @@
#include "qquickmultipointtoucharea_p.h"
#include <QtQuick/qquickwindow.h>
#include <private/qsgadaptationlayer_p.h>
-#include <private/qevent_p.h>
#include <private/qquickitem_p.h>
#include <private/qquickwindow_p.h>
#include <private/qguiapplication_p.h>
#include <QtGui/private/qevent_p.h>
+#include <QtGui/private/qeventpoint_p.h>
#include <QtGui/private/qpointingdevice_p.h>
#include <QEvent>
#include <QMouseEvent>
@@ -570,7 +570,7 @@ void QQuickMultiPointTouchArea::grabGesture(QPointingDevice *dev)
setKeepTouchGrab(true);
}
-void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
+void QQuickMultiPointTouchArea::updateTouchData(QEvent *event, RemapEventPoints remap)
{
bool ended = false;
bool moved = false;
@@ -578,6 +578,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
clearTouchLists();
QList<QEventPoint> touchPoints;
+ bool touchPointsFromEvent = false;
QPointingDevice *dev = nullptr;
switch (event->type()) {
@@ -586,6 +587,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
case QEvent::TouchEnd: {
QTouchEvent* te = static_cast<QTouchEvent*>(event);
touchPoints = te->points();
+ touchPointsFromEvent = true;
dev = const_cast<QPointingDevice *>(te->pointingDevice());
break;
}
@@ -630,6 +632,8 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
}
if (numTouchPoints >= _minimumTouchPoints && numTouchPoints <= _maximumTouchPoints) {
for (QEventPoint &p : touchPoints) {
+ if (touchPointsFromEvent && remap == RemapEventPoints::ToLocal)
+ QMutableEventPoint::from(p).setPosition(mapFromScene(p.scenePosition()));
QEventPoint::State touchPointState = p.state();
int id = p.id();
if (touchPointState & QEventPoint::State::Released) {
@@ -971,12 +975,12 @@ bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *receiver, QEve
}
if (!shouldFilter(event))
return false;
- updateTouchData(event);
+ updateTouchData(event, RemapEventPoints::ToLocal);
return _stealMouse;
case QEvent::TouchEnd: {
if (!shouldFilter(event))
return false;
- updateTouchData(event);
+ updateTouchData(event, RemapEventPoints::ToLocal);
ungrab(true);
}
break;
diff --git a/src/quick/items/qquickmultipointtoucharea_p.h b/src/quick/items/qquickmultipointtoucharea_p.h
index 66626e8610..e1f2c559ce 100644
--- a/src/quick/items/qquickmultipointtoucharea_p.h
+++ b/src/quick/items/qquickmultipointtoucharea_p.h
@@ -278,7 +278,8 @@ protected:
void updateTouchPoint(QQuickTouchPoint*, const QEventPoint*);
void updateTouchPoint(QQuickTouchPoint *dtp, const QMouseEvent *e);
- void updateTouchData(QEvent*);
+ enum class RemapEventPoints { No, ToLocal };
+ void updateTouchData(QEvent*, RemapEventPoints remap = RemapEventPoints::No);
bool sendMouseEvent(QMouseEvent *event);
bool shouldFilter(QEvent *event);
diff --git a/src/quick/items/qquickpainteditem.cpp b/src/quick/items/qquickpainteditem.cpp
index 2cf26142c8..c24a6cbfdd 100644
--- a/src/quick/items/qquickpainteditem.cpp
+++ b/src/quick/items/qquickpainteditem.cpp
@@ -83,6 +83,8 @@ public:
\note It important to understand the performance implications such items
can incur. See QQuickPaintedItem::RenderTarget and
QQuickPaintedItem::renderTarget.
+
+ \sa {Scene Graph - Painted Item}, {Writing QML Extensions with C++}
*/
/*!
diff --git a/src/quick/items/qquickpalette.cpp b/src/quick/items/qquickpalette.cpp
index a64b309426..5e95a92238 100644
--- a/src/quick/items/qquickpalette.cpp
+++ b/src/quick/items/qquickpalette.cpp
@@ -334,3 +334,5 @@ bool QQuickPalette::isValidColorGroup(QPalette::ColorGroup groupTag,
}
QT_END_NAMESPACE
+
+#include "moc_qquickpalette_p.cpp"
diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp
index d862eba25b..f714b98309 100644
--- a/src/quick/items/qquickpathview.cpp
+++ b/src/quick/items/qquickpathview.cpp
@@ -49,7 +49,8 @@
#include <private/qqmlchangeset_p.h>
#include <QtQml/qqmlinfo.h>
-#include <QtGui/qevent.h>
+
+#include <QtGui/private/qeventpoint_p.h>
#include <QtGui/qevent.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qstylehints.h>
@@ -145,7 +146,8 @@ QQuickItem *QQuickPathViewPrivate::getItem(int modelIndex, qreal z, bool async)
item->setParentItem(q);
requestedIndex = -1;
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
- itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
+ itemPrivate->addItemChangeListener(
+ this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
}
inRequest = false;
return item;
@@ -198,11 +200,14 @@ void QQuickPathView::initItem(int index, QObject *object)
void QQuickPathViewPrivate::releaseItem(QQuickItem *item)
{
- if (!item || !model)
+ if (!item)
return;
qCDebug(lcItemViewDelegateLifecycle) << "release" << item;
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
- itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
+ itemPrivate->removeItemChangeListener(
+ this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
+ if (!model)
+ return;
QQmlInstanceModel::ReleaseFlags flags = model->release(item);
if (!flags) {
// item was not destroyed, and we no longer reference it.
diff --git a/src/quick/items/qquickpathview_p_p.h b/src/quick/items/qquickpathview_p_p.h
index 274086ea7c..b83cd95b95 100644
--- a/src/quick/items/qquickpathview_p_p.h
+++ b/src/quick/items/qquickpathview_p_p.h
@@ -88,6 +88,12 @@ public:
}
}
+ void itemDestroyed(QQuickItem *item) override
+ {
+ if (!items.removeOne(item))
+ itemCache.removeOne(item);
+ }
+
void scheduleLayout() {
Q_Q(QQuickPathView);
if (!layoutScheduled) {
diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp
index ed8506b440..37458542d3 100644
--- a/src/quick/items/qquickpincharea.cpp
+++ b/src/quick/items/qquickpincharea.cpp
@@ -360,7 +360,7 @@ void QQuickPinchArea::touchEvent(QTouchEvent *event)
void QQuickPinchArea::clearPinch(QTouchEvent *event)
{
Q_D(QQuickPinchArea);
- qCDebug(lcPA, "clear: %lld touchpoints", d->touchPoints.count());
+ qCDebug(lcPA, "clear: %" PRIdQSIZETYPE " touchpoints", d->touchPoints.count());
d->touchPoints.clear();
if (d->inPinch) {
d->inPinch = false;
@@ -390,12 +390,13 @@ void QQuickPinchArea::clearPinch(QTouchEvent *event)
}
}
setKeepTouchGrab(false);
+ setKeepMouseGrab(false);
}
void QQuickPinchArea::cancelPinch(QTouchEvent *event)
{
Q_D(QQuickPinchArea);
- qCDebug(lcPA, "cancel: %lld touchpoints", d->touchPoints.count());
+ qCDebug(lcPA, "cancel: %" PRIdQSIZETYPE " touchpoints", d->touchPoints.count());
d->touchPoints.clear();
if (d->inPinch) {
d->inPinch = false;
@@ -431,6 +432,7 @@ void QQuickPinchArea::cancelPinch(QTouchEvent *event)
event->setExclusiveGrabber(point, nullptr);
}
setKeepTouchGrab(false);
+ setKeepMouseGrab(false);
}
void QQuickPinchArea::updatePinch(QTouchEvent *event, bool filtering)
@@ -440,6 +442,7 @@ void QQuickPinchArea::updatePinch(QTouchEvent *event, bool filtering)
if (d->touchPoints.count() < 2) {
// A pinch gesture is not occurring, so stealing the grab is permitted.
setKeepTouchGrab(false);
+ setKeepMouseGrab(false);
// During filtering, there's no need to hold a grab for one point,
// because filtering happens for every event anyway.
// But if we receive the event via direct delivery, and give up the grab,
@@ -463,6 +466,8 @@ void QQuickPinchArea::updatePinch(QTouchEvent *event, bool filtering)
pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
pe.setPoint1(mapFromScene(d->lastPoint1));
pe.setPoint2(mapFromScene(d->lastPoint2));
+ setKeepTouchGrab(false);
+ setKeepMouseGrab(false);
emit pinchFinished(&pe);
d->pinchStartDist = 0;
d->pinchActivated = false;
@@ -499,6 +504,8 @@ void QQuickPinchArea::updatePinch(QTouchEvent *event, bool filtering)
d->initPinch = true;
event->setExclusiveGrabber(touchPoint1, this);
event->setExclusiveGrabber(touchPoint2, this);
+ setKeepTouchGrab(true);
+ setKeepMouseGrab(true);
}
if (d->pinchActivated && !d->pinchRejected) {
const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
@@ -561,6 +568,9 @@ void QQuickPinchArea::updatePinch(QTouchEvent *event, bool filtering)
event->setExclusiveGrabber(touchPoint1, this);
event->setExclusiveGrabber(touchPoint2, this);
setKeepTouchGrab(true);
+ // So that PinchArea works in PathView, grab mouse events too.
+ // We should be able to remove these setKeepMouseGrab calls when QTBUG-105567 is fixed.
+ setKeepMouseGrab(true);
d->inPinch = true;
if (d->pinch && d->pinch->target()) {
auto targetParent = pinch()->target()->parentItem();
@@ -727,6 +737,8 @@ bool QQuickPinchArea::event(QEvent *event)
clearPinch(nullptr);
break;
case Qt::ZoomNativeGesture: {
+ if (d->pinchRejected)
+ break;
qreal scale = d->pinchLastScale * (1.0 + gesture->value());
QQuickPinchEvent pe(d->pinchStartCenter, scale, d->pinchLastAngle, 0.0);
pe.setStartCenter(d->pinchStartCenter);
@@ -744,7 +756,10 @@ bool QQuickPinchArea::event(QEvent *event)
else
emit pinchStarted(&pe);
d->inPinch = true;
- updatePinchTarget();
+ if (pe.accepted())
+ updatePinchTarget();
+ else
+ d->pinchRejected = true;
} break;
case Qt::SmartZoomNativeGesture: {
if (gesture->value() > 0.0 && d->pinch && d->pinch->target()) {
@@ -768,6 +783,8 @@ bool QQuickPinchArea::event(QEvent *event)
emit smartZoom(&pe);
} break;
case Qt::RotateNativeGesture: {
+ if (d->pinchRejected)
+ break;
qreal angle = d->pinchLastAngle + gesture->value();
QQuickPinchEvent pe(d->pinchStartCenter, d->pinchLastScale, angle, 0.0);
pe.setStartCenter(d->pinchStartCenter);
@@ -786,7 +803,10 @@ bool QQuickPinchArea::event(QEvent *event)
emit pinchStarted(&pe);
d->inPinch = true;
d->pinchRotation = angle;
- updatePinchTarget();
+ if (pe.accepted())
+ updatePinchTarget();
+ else
+ d->pinchRejected = true;
} break;
default:
return QQuickItem::event(event);
diff --git a/src/quick/items/qquickpositioners.cpp b/src/quick/items/qquickpositioners.cpp
index a8d050527b..313143e881 100644
--- a/src/quick/items/qquickpositioners.cpp
+++ b/src/quick/items/qquickpositioners.cpp
@@ -1697,7 +1697,7 @@ void QQuickGrid::doPositioning(QSizeF *contentSize)
QQuickBasePositionerPrivate *d = static_cast<QQuickBasePositionerPrivate*>(QQuickBasePositionerPrivate::get(this));
int c = m_columns;
int r = m_rows;
- int numVisible = positionedItems.count();
+ const int numVisible = positionedItems.count();
if (m_columns <= 0 && m_rows <= 0) {
c = 4;
@@ -1714,6 +1714,10 @@ void QQuickGrid::doPositioning(QSizeF *contentSize)
return; //Nothing else to do
}
+ if (numVisible > r * c) {
+ qmlWarning(this) << "Grid contains more visible items (" << numVisible << ") than rows*columns (" << r * c << ")";
+ }
+
QList<qreal> maxColWidth;
QList<qreal> maxRowHeight;
int childIndex =0;
diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp
index ce570a743c..52c830c14a 100644
--- a/src/quick/items/qquickrectangle.cpp
+++ b/src/quick/items/qquickrectangle.cpp
@@ -578,8 +578,13 @@ QSGNode *QQuickRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
if (d->pen && d->pen->isValid()) {
rectangle->setPenColor(d->pen->color());
- rectangle->setPenWidth(d->pen->width());
- rectangle->setAligned(d->pen->pixelAligned());
+ qreal penWidth = d->pen->width();
+ if (d->pen->pixelAligned()) {
+ qreal dpr = window() ? window()->effectiveDevicePixelRatio() : 1.0;
+ penWidth = qRound(penWidth * dpr) / dpr; // Ensures integer width after dpr scaling
+ }
+ rectangle->setPenWidth(penWidth);
+ rectangle->setAligned(false); // width rounding already done, so the Node should not do it
} else {
rectangle->setPenWidth(0);
}
diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp
index a660bd0ca3..2d48a39be8 100644
--- a/src/quick/items/qquickrendercontrol.cpp
+++ b/src/quick/items/qquickrendercontrol.cpp
@@ -181,6 +181,14 @@ QQuickRenderControl::QQuickRenderControl(QObject *parent)
}
/*!
+ \internal
+*/
+QQuickRenderControl::QQuickRenderControl(QQuickRenderControlPrivate &dd, QObject * parent)
+ : QObject(dd, parent)
+{
+}
+
+/*!
Destroys the instance. Releases all scenegraph resources.
\sa invalidate()
@@ -214,7 +222,7 @@ void QQuickRenderControlPrivate::windowDestroyed()
QQuickWindowPrivate::get(window)->animationController.reset();
#if QT_CONFIG(quick_shadereffect)
- QSGRhiShaderEffectNode::cleanupMaterialTypeCache();
+ QSGRhiShaderEffectNode::cleanupMaterialTypeCache(window);
#endif
window = nullptr;
}
@@ -322,6 +330,7 @@ bool QQuickRenderControl::initialize()
params.initialSurfacePixelSize = d->window->size() * d->window->effectiveDevicePixelRatio();
params.maybeSurface = d->window;
renderContext->initialize(&params);
+ d->initialized = true;
} else {
qWarning("QRhi is only compatible with default adaptation");
return false;
@@ -528,7 +537,7 @@ void QQuickRenderControlPrivate::maybeUpdate()
Reimplemented in subclasses to return the real window this render control
is rendering into.
- If \a offset in non-null, it is set to the offset of the control
+ If \a offset is non-null, it is set to the offset of the control
inside the window.
\note While not mandatory, reimplementing this function becomes essential for
@@ -540,7 +549,7 @@ void QQuickRenderControlPrivate::maybeUpdate()
/*!
Returns the real window that \a win is being rendered to, if any.
- If \a offset in non-null, it is set to the offset of the rendering
+ If \a offset is non-null, it is set to the offset of the rendering
inside its window.
*/
@@ -554,6 +563,14 @@ QWindow *QQuickRenderControl::renderWindowFor(QQuickWindow *win, QPoint *offset)
return nullptr;
}
+bool QQuickRenderControlPrivate::isRenderWindowFor(QQuickWindow *quickWin, const QWindow *renderWin)
+{
+ QQuickRenderControl *rc = QQuickWindowPrivate::get(quickWin)->renderControl;
+ if (rc)
+ return QQuickRenderControlPrivate::get(rc)->isRenderWindow(renderWin);
+ return false;
+}
+
/*!
\return the QQuickWindow this QQuickRenderControl is associated with.
diff --git a/src/quick/items/qquickrendercontrol.h b/src/quick/items/qquickrendercontrol.h
index 160aad73ab..3b1646c3f6 100644
--- a/src/quick/items/qquickrendercontrol.h
+++ b/src/quick/items/qquickrendercontrol.h
@@ -80,6 +80,9 @@ public:
QQuickWindow *window() const;
+protected:
+ QQuickRenderControl(QQuickRenderControlPrivate &dd, QObject * parent);
+
Q_SIGNALS:
void renderRequested();
void sceneChanged();
diff --git a/src/quick/items/qquickrendercontrol_p.h b/src/quick/items/qquickrendercontrol_p.h
index 00d9c909d0..8349153df0 100644
--- a/src/quick/items/qquickrendercontrol_p.h
+++ b/src/quick/items/qquickrendercontrol_p.h
@@ -71,6 +71,9 @@ public:
return renderControl->d_func();
}
+ static bool isRenderWindowFor(QQuickWindow *quickWin, const QWindow *renderWin);
+ virtual bool isRenderWindow(const QWindow *w) { Q_UNUSED(w); return false; }
+
static void cleanup();
void windowDestroyed();
diff --git a/src/quick/items/qquickscalegrid.cpp b/src/quick/items/qquickscalegrid.cpp
index c2288cf220..13b249d50f 100644
--- a/src/quick/items/qquickscalegrid.cpp
+++ b/src/quick/items/qquickscalegrid.cpp
@@ -217,3 +217,5 @@ QString QQuickGridScaledImage::pixmapUrl() const
}
QT_END_NAMESPACE
+
+#include "moc_qquickscalegrid_p_p.cpp"
diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp
index caabd541b6..5c2f113fdc 100644
--- a/src/quick/items/qquickshadereffect.cpp
+++ b/src/quick/items/qquickshadereffect.cpp
@@ -1165,6 +1165,8 @@ QSGNode *QQuickShaderEffectImpl::handleUpdatePaintNode(QSGNode *oldNode, QQuickI
sd.fragment.shader = &m_shaders[Fragment];
sd.fragment.dirtyConstants = &m_dirtyConstants[Fragment];
sd.fragment.dirtyTextures = &m_dirtyTextures[Fragment];
+ sd.materialTypeCacheKey = m_item->window();
+
node->syncMaterial(&sd);
if (m_dirty & QSGShaderEffectNode::DirtyShaderMesh) {
@@ -1243,6 +1245,7 @@ bool QQuickShaderEffectImpl::updateUniformValue(const QByteArray &name, const QV
sd.fragment.shader = &m_shaders[Fragment];
sd.fragment.dirtyConstants = &dirtyConstants[Fragment];
sd.fragment.dirtyTextures = {};
+ sd.materialTypeCacheKey = m_item->window();
node->syncMaterial(&sd);
@@ -1384,7 +1387,7 @@ bool QQuickShaderEffectImpl::updateShader(Shader shaderType, const QUrl &fileUrl
// provided and monitored like with an application-provided shader.
QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
v.name = QByteArrayLiteral("source");
- v.bindPoint = 0; // fake
+ v.bindPoint = 1; // fake, must match the default source bindPoint in qquickshadereffectnode.cpp
v.type = texturesSeparate ? QSGGuiThreadShaderEffectManager::ShaderInfo::Texture
: QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler;
m_shaders[shaderType].shaderInfo.variables.append(v);
@@ -1438,10 +1441,8 @@ void QQuickShaderEffectImpl::updateShaderVars(Shader shaderType)
const int varCount = m_shaders[shaderType].shaderInfo.variables.count();
m_shaders[shaderType].varData.resize(varCount);
- // Reuse signal mappers as much as possible since the mapping is based on
- // the index and shader type which are both constant.
- if (m_mappers[shaderType].count() < varCount)
- m_mappers[shaderType].resize(varCount);
+ // Recreate signal mappers when the shader has changed.
+ clearMappers(shaderType);
auto *engine = qmlEngine(m_item);
QQmlPropertyCache *propCache = engine ? QQmlData::ensurePropertyCache(engine, m_item) : nullptr;
@@ -1497,13 +1498,11 @@ void QQuickShaderEffectImpl::updateShaderVars(Shader shaderType)
if (pd->notifyIndex() == -1) {
qWarning("QQuickShaderEffect: property '%s' does not have notification method!", v.name.constData());
} else {
- auto *&mapper = m_mappers[shaderType][i];
- if (!mapper) {
- const int mappedId = indexToMappedId(shaderType, i);
- mapper = new QtPrivate::EffectSlotMapper([this, mappedId](){
- this->propertyChanged(mappedId);
- });
- }
+ const int mappedId = indexToMappedId(shaderType, i);
+ auto mapper = new QtPrivate::EffectSlotMapper([this, mappedId](){
+ this->propertyChanged(mappedId);
+ });
+ m_mappers[shaderType].append(mapper);
mapper->setSignalIndex(m_itemMetaObject->property(propIdx).notifySignal().methodIndex());
Q_ASSERT(m_item->metaObject() == m_itemMetaObject);
bool ok = QObjectPrivate::connectImpl(m_item, pd->notifyIndex(), m_item, nullptr, mapper,
diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp
index 4f61d61309..b298ed74da 100644
--- a/src/quick/items/qquickshadereffectsource.cpp
+++ b/src/quick/items/qquickshadereffectsource.cpp
@@ -344,7 +344,6 @@ void QQuickShaderEffectSource::setSourceItem(QQuickItem *item)
d->refFromEffectItem(m_hideSource);
d->addItemChangeListener(this, QQuickItemPrivate::Geometry);
connect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
- connect(m_sourceItem, SIGNAL(parentChanged(QQuickItem*)), this, SLOT(sourceItemParentChanged(QQuickItem*)));
} else {
qWarning("ShaderEffectSource: sourceItem and ShaderEffectSource must both be children of the same window.");
m_sourceItem = nullptr;
@@ -364,13 +363,6 @@ void QQuickShaderEffectSource::sourceItemDestroyed(QObject *item)
}
-void QQuickShaderEffectSource::sourceItemParentChanged(QQuickItem *parent)
-{
- if (!parent && m_texture)
- m_texture->setItem(0);
-}
-
-
/*!
\qmlproperty rect QtQuick::ShaderEffectSource::sourceRect
diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h
index 921038e49a..fe419e5959 100644
--- a/src/quick/items/qquickshadereffectsource_p.h
+++ b/src/quick/items/qquickshadereffectsource_p.h
@@ -174,7 +174,6 @@ Q_SIGNALS:
private Q_SLOTS:
void sourceItemDestroyed(QObject *item);
void invalidateSceneGraph();
- void sourceItemParentChanged(QQuickItem *parent);
protected:
void releaseResources() override;
diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp
index e35c766ab6..ddc43c10ed 100644
--- a/src/quick/items/qquickspriteengine.cpp
+++ b/src/quick/items/qquickspriteengine.cpp
@@ -352,6 +352,7 @@ void QQuickSpriteEngine::startAssemblingImage()
return;
m_loaded = false;
m_errorsPrinted = false;
+ m_sprites.clear();
//This could also trigger the start of the image loading in Sprites, however that currently happens in Sprite::setSource
diff --git a/src/quick/items/qquickstateoperations.cpp b/src/quick/items/qquickstateoperations.cpp
index bfec619f80..d16f0d0883 100644
--- a/src/quick/items/qquickstateoperations.cpp
+++ b/src/quick/items/qquickstateoperations.cpp
@@ -359,8 +359,7 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
actions << xa;
} else {
QQmlProperty property(d->target, QLatin1String("x"));
- QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->xString.value, d->target, qmlContext(this));
- newBinding->setTarget(property);
+ auto newBinding = QQmlAnyBinding::createFromScriptString(property, d->xString.value, d->target, qmlContext(this));
QQuickStateAction xa;
xa.property = property;
xa.toBinding = newBinding;
@@ -378,8 +377,7 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
actions << ya;
} else {
QQmlProperty property(d->target, QLatin1String("y"));
- QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->yString.value, d->target, qmlContext(this));
- newBinding->setTarget(property);
+ auto newBinding = QQmlAnyBinding::createFromScriptString(property, d->yString.value, d->target, qmlContext(this));
QQuickStateAction ya;
ya.property = property;
ya.toBinding = newBinding;
@@ -397,8 +395,7 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
actions << sa;
} else {
QQmlProperty property(d->target, QLatin1String("scale"));
- QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->scaleString.value, d->target, qmlContext(this));
- newBinding->setTarget(property);
+ auto newBinding = QQmlAnyBinding::createFromScriptString(property, d->scaleString.value, d->target, qmlContext(this));
QQuickStateAction sa;
sa.property = property;
sa.toBinding = newBinding;
@@ -416,8 +413,7 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
actions << ra;
} else {
QQmlProperty property(d->target, QLatin1String("rotation"));
- QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->rotationString.value, d->target, qmlContext(this));
- newBinding->setTarget(property);
+ auto newBinding = QQmlAnyBinding::createFromScriptString(property, d->rotationString.value, d->target, qmlContext(this));
QQuickStateAction ra;
ra.property = property;
ra.toBinding = newBinding;
@@ -435,8 +431,7 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
actions << wa;
} else {
QQmlProperty property(d->target, QLatin1String("width"));
- QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->widthString.value, d->target, qmlContext(this));
- newBinding->setTarget(property);
+ auto newBinding = QQmlAnyBinding::createFromScriptString(property, d->widthString, d->target, qmlContext(this));
QQuickStateAction wa;
wa.property = property;
wa.toBinding = newBinding;
@@ -454,8 +449,7 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
actions << ha;
} else {
QQmlProperty property(d->target, QLatin1String("height"));
- QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->heightString.value, d->target, qmlContext(this));
- newBinding->setTarget(property);
+ auto newBinding = QQmlAnyBinding::createFromScriptString(property, d->heightString, d->target, qmlContext(this));
QQuickStateAction ha;
ha.property = property;
ha.toBinding = newBinding;
@@ -492,11 +486,13 @@ void QQuickParentChangePrivate::reverseRewindHelper(const std::unique_ptr<QQuick
{
if (!target || !snapshot)
return;
- target->setX(snapshot->x);
- target->setY(snapshot->y);
+
+ // leave existing bindings alive; new bindings are applied in applyBindings
+ // setPosition and setSize update the geometry without invalidating bindings
+ target->setPosition(QPointF(snapshot->x, snapshot->y));
+ target->setSize(QSizeF(snapshot->width, snapshot->height));
+
target->setScale(snapshot->scale);
- target->setWidth(snapshot->width);
- target->setHeight(snapshot->height);
target->setRotation(snapshot->rotation);
target->setParentItem(snapshot->parent);
if (snapshot->stackBefore)
@@ -573,7 +569,7 @@ void QQuickParentChange::rewind()
The AnchorChanges type is used to modify the anchors of an item in a \l State.
AnchorChanges cannot be used to modify the margins on an item. For this, use
- PropertyChanges intead.
+ PropertyChanges instead.
In the following example we change the top and bottom anchors of an item
using AnchorChanges, and the top and bottom anchor margins using
@@ -1106,6 +1102,7 @@ void QQuickAnchorChanges::reverse()
QQuickAnchors::Anchors stateHAnchors = d->anchorSet->d_func()->usedAnchors & QQuickAnchors::Horizontal_Mask;
QQuickAnchors::Anchors origHAnchors = targetPrivate->anchors()->usedAnchors() & QQuickAnchors::Horizontal_Mask;
+ const QRectF oldGeometry(d->target->position(), d->target->size());
bool stateSetWidth = (stateHAnchors &&
stateHAnchors != QQuickAnchors::LeftAnchor &&
stateHAnchors != QQuickAnchors::RightAnchor &&
@@ -1114,8 +1111,11 @@ void QQuickAnchorChanges::reverse()
origHAnchors != QQuickAnchors::LeftAnchor &&
origHAnchors != QQuickAnchors::RightAnchor &&
origHAnchors != QQuickAnchors::HCenterAnchor);
- if (d->origWidth.isValid() && stateSetWidth && !origSetWidth)
- d->target->setWidth(d->origWidth.value);
+ if (d->origWidth.isValid() && stateSetWidth && !origSetWidth && !qt_is_nan(d->origWidth)) {
+ targetPrivate->widthValidFlag = true;
+ if (targetPrivate->width != d->origWidth)
+ targetPrivate->width.setValueBypassingBindings(d->origWidth);
+ }
bool stateSetHeight = (stateVAnchors &&
stateVAnchors != QQuickAnchors::TopAnchor &&
@@ -1127,14 +1127,23 @@ void QQuickAnchorChanges::reverse()
origVAnchors != QQuickAnchors::BottomAnchor &&
origVAnchors != QQuickAnchors::VCenterAnchor &&
origVAnchors != QQuickAnchors::BaselineAnchor);
- if (d->origHeight.isValid() && stateSetHeight && !origSetHeight)
- d->target->setHeight(d->origHeight.value);
+ if (d->origHeight.isValid() && stateSetHeight && !origSetHeight && !!qt_is_nan(d->origHeight)) {
+ targetPrivate->heightValidFlag = true;
+ if (targetPrivate->height != d->origHeight)
+ targetPrivate->height.setValueBypassingBindings(d->origHeight);
+ }
+
+ if (stateHAnchors && !origHAnchors && !qt_is_nan(d->origX) && d->origX != targetPrivate->x)
+ targetPrivate->x.setValueBypassingBindings(d->origX);
- if (stateHAnchors && !origHAnchors)
- d->target->setX(d->origX);
+ if (stateVAnchors && !origVAnchors && !qt_is_nan(d->origY) && d->origY != targetPrivate->y)
+ targetPrivate->y.setValueBypassingBindings(d->origY);
- if (stateVAnchors && !origVAnchors)
- d->target->setY(d->origY);
+ const QRectF newGeometry(d->target->position(), d->target->size());
+ if (newGeometry != oldGeometry) {
+ targetPrivate->dirty(QQuickItemPrivate::Position);
+ d->target->geometryChange(newGeometry, oldGeometry);
+ }
}
QQuickStateActionEvent::EventType QQuickAnchorChanges::type() const
@@ -1328,15 +1337,31 @@ void QQuickAnchorChanges::rewind()
return;
QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(d->target);
+ const QRectF oldGeometry(d->target->position(), d->target->size());
+
+ // Restore previous values (but not previous bindings, i.e. anchors).
+ // Also, don't drop any new bindings.
+ if (!qt_is_nan(d->rewindX) && d->rewindX != targetPrivate->x)
+ targetPrivate->x.setValueBypassingBindings(d->rewindX);
+ if (!qt_is_nan(d->rewindY) && d->rewindY != targetPrivate->y)
+ targetPrivate->y.setValueBypassingBindings(d->rewindY);
+
+ if (targetPrivate->widthValid() && !qt_is_nan(d->rewindWidth)) {
+ targetPrivate->widthValidFlag = true;
+ if (d->rewindWidth != targetPrivate->width)
+ targetPrivate->width.setValueBypassingBindings(d->rewindWidth);
+ }
- //restore previous values (but not previous bindings, i.e. anchors)
- d->target->setX(d->rewindX);
- d->target->setY(d->rewindY);
- if (targetPrivate->widthValid()) {
- d->target->setWidth(d->rewindWidth);
+ if (targetPrivate->heightValid() && !qt_is_nan(d->rewindHeight)) {
+ targetPrivate->heightValidFlag = true;
+ if (d->rewindHeight != targetPrivate->height)
+ targetPrivate->height.setValueBypassingBindings(d->rewindHeight);
}
- if (targetPrivate->heightValid()) {
- d->target->setHeight(d->rewindHeight);
+
+ const QRectF newGeometry(d->target->position(), d->target->size());
+ if (newGeometry != oldGeometry) {
+ targetPrivate->dirty(QQuickItemPrivate::Position);
+ d->target->geometryChange(newGeometry, oldGeometry);
}
}
@@ -1373,7 +1398,6 @@ void QQuickAnchorChanges::saveTargetValues()
d->toHeight = d->target->height();
}
-#include <moc_qquickstateoperations_p.cpp>
-
QT_END_NAMESPACE
+#include <moc_qquickstateoperations_p.cpp>
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index 540f2edfde..9f259166df 100644
--- a/src/quick/items/qquicktableview.cpp
+++ b/src/quick/items/qquicktableview.cpp
@@ -429,6 +429,7 @@
/*!
\qmlproperty ItemSelectionModel QtQuick::TableView::selectionModel
+ \since 6.2
This property can be set to control which delegate items should be shown as
selected. If the delegate has a \c {required property bool selected}
@@ -583,8 +584,8 @@
there is no setter function. This getter function is mostly
useful if the TableView doesn't have a columnWidthProvider set, since
otherwise you can call that function instead (which will work, even
- for columns that are not currently visible.
- If no columnWidthProvider is set, the height of a row will be
+ for columns that are not currently visible).
+ If no columnWidthProvider is set, the width of a column will be
equal to its \l implicitColumnWidth().
\sa columnWidthProvider, implicitColumnWidth(), isColumnLoaded(), {Row heights and column widths}
@@ -602,7 +603,7 @@
there is no setter function. This getter function is mostly
useful if the TableView doesn't have a rowHeightProvider set, since
otherwise you can call that function instead (which will work, even
- for rows that are not currently visible.
+ for rows that are not currently visible).
If no rowHeightProvider is set, the height of a row will be
equal to its \l implicitRowHeight().
@@ -687,8 +688,6 @@ Q_LOGGING_CATEGORY(lcTableViewDelegateLifecycle, "qt.quick.tableview.lifecycle")
#define Q_TABLEVIEW_ASSERT(cond, output) Q_ASSERT((cond) || [&](){ dumpTable(); qWarning() << "output:" << output; return false;}())
static const Qt::Edge allTableEdges[] = { Qt::LeftEdge, Qt::RightEdge, Qt::TopEdge, Qt::BottomEdge };
-static const int kEdgeIndexNotSet = -2;
-static const int kEdgeIndexAtEnd = -3;
static const char* kRequiredProperty = "_qt_isrequiredpropery_selected";
@@ -790,8 +789,12 @@ void QQuickTableViewPrivate::setSelectionStartPos(const QPointF &pos)
{
if (loadedItems.isEmpty())
return;
- if (!selectionModel)
+ if (!selectionModel) {
+ if (warnNoSelectionModel)
+ qmlWarning(q_func()) << "Cannot set selection: no SelectionModel assigned!";
+ warnNoSelectionModel = false;
return;
+ }
const QAbstractItemModel *qaim = selectionModel->model();
if (!qaim)
return;
@@ -816,8 +819,12 @@ void QQuickTableViewPrivate::setSelectionEndPos(const QPointF &pos)
{
if (loadedItems.isEmpty())
return;
- if (!selectionModel)
+ if (!selectionModel) {
+ if (warnNoSelectionModel)
+ qmlWarning(q_func()) << "Cannot set selection: no SelectionModel assigned!";
+ warnNoSelectionModel = false;
return;
+ }
const QAbstractItemModel *qaim = selectionModel->model();
if (!qaim)
return;
@@ -2331,16 +2338,31 @@ void QQuickTableViewPrivate::processRebuildTable()
if (rebuildState == RebuildState::LayoutTable) {
layoutAfterLoadingInitialTable();
+ loadAndUnloadVisibleEdges();
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::CancelOvershootBottomRight) {
+ cancelOvershootBottomRight();
+ loadAndUnloadVisibleEdges();
if (!moveToNextRebuildState())
return;
}
- if (rebuildState == RebuildState::LoadAndUnloadAfterLayout) {
+ if (rebuildState == RebuildState::CancelOvershootTopLeft) {
+ cancelOvershootTopLeft();
loadAndUnloadVisibleEdges();
if (!moveToNextRebuildState())
return;
}
+ if (rebuildState == RebuildState::UpdateContentSize) {
+ updateContentSize();
+ if (!moveToNextRebuildState())
+ return;
+ }
+
const bool preload = (rebuildOptions & RebuildOption::All
&& reusableFlag == QQmlTableInstanceModel::Reusable);
@@ -2584,23 +2606,31 @@ void QQuickTableViewPrivate::loadInitialTable()
loadAndUnloadVisibleEdges();
}
-void QQuickTableViewPrivate::layoutAfterLoadingInitialTable()
+void QQuickTableViewPrivate::updateContentSize()
{
- clearEdgeSizeCache();
- relayoutTableItems();
- syncLoadedTableRectFromLoadedTable();
-
- if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentWidth) || allColumnsLoaded()) {
+ const bool allColumnsLoaded = atTableEnd(Qt::LeftEdge) && atTableEnd(Qt::RightEdge);
+ if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentWidth) || allColumnsLoaded) {
updateAverageColumnWidth();
updateContentWidth();
}
- if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentHeight) || allRowsLoaded()) {
+ const bool allRowsLoaded = atTableEnd(Qt::TopEdge) && atTableEnd(Qt::BottomEdge);
+ if (rebuildOptions.testFlag(RebuildOption::CalculateNewContentHeight) || allRowsLoaded) {
updateAverageRowHeight();
updateContentHeight();
}
updateExtents();
+}
+
+void QQuickTableViewPrivate::layoutAfterLoadingInitialTable()
+{
+ clearEdgeSizeCache();
+ relayoutTableItems();
+ syncLoadedTableRectFromLoadedTable();
+
+ updateContentSize();
+
adjustViewportXAccordingToAlignment();
adjustViewportYAccordingToAlignment();
}
@@ -2675,6 +2705,58 @@ void QQuickTableViewPrivate::adjustViewportYAccordingToAlignment()
syncViewportRect();
}
+void QQuickTableViewPrivate::cancelOvershootBottomRight()
+{
+ // After doing a layout with positioning specified, the table might end up in an
+ // overshooting state (meaning that the table is dragged beyond the bounds
+ // of the viewport, with the result that it will bounce back in once you touch it).
+ // This happens because the layouting might try to e.g position the last row in the
+ // model in the center of the viewport, which will leave a big blank space from the
+ // last row towards the bottom.
+ // This space will be detected by updateExtents(), which will adjust the extents so
+ // that the superfluous blank area ends up as an overshoot. But we shouldn't leave the
+ // table in an overshooting state after a rebuild, so we clamp it here so thatit's
+ // either aligned to the top/left or to the bottom/right of the viewport.
+ // An exception is if we're not rebuilding because of positionViewAtCell(), since
+ // the app is allowed to set contentX and contentY at start-up. A second exception is
+ // if this view is a sync child, since then we can end up with extra space at the end
+ // if our model has fewer rows/columns than the syncView.
+ const qreal blankSpaceRight = viewportRect.right() - loadedTableOuterRect.right();
+ const qreal blankSpaceBottom = viewportRect.bottom() - loadedTableOuterRect.bottom();
+ const bool positionVertically = rebuildOptions.testFlag(RebuildOption::PositionViewAtRow);
+ const bool positionHorizontally = rebuildOptions.testFlag(RebuildOption::PositionViewAtColumn);
+
+ if (positionVertically && !syncVertically && viewportRect.top() > 0 && blankSpaceBottom > 0) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "cancelling overshoot at bottom:" << blankSpaceBottom;
+ setLocalViewportY(viewportRect.y() - blankSpaceBottom);
+ syncViewportRect();
+ }
+
+ if (positionHorizontally && !syncHorizontally && viewportRect.left() > 0 && blankSpaceRight > 0) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "cancelling overshoot at right:" << blankSpaceRight;
+ setLocalViewportX(viewportRect.x() - blankSpaceRight);
+ syncViewportRect();
+ }
+}
+
+void QQuickTableViewPrivate::cancelOvershootTopLeft()
+{
+ const bool positionVertically = rebuildOptions.testFlag(RebuildOption::PositionViewAtRow);
+ const bool positionHorizontally = rebuildOptions.testFlag(RebuildOption::PositionViewAtColumn);
+
+ if (positionVertically && !syncVertically && viewportRect.top() < 0) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "cancelling overshoot at top:" << viewportRect.top();
+ setLocalViewportY(0);
+ syncViewportRect();
+ }
+
+ if (positionHorizontally && !syncHorizontally && viewportRect.left() < 0) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "cancelling overshoot at left:" << viewportRect.left();
+ setLocalViewportX(0);
+ syncViewportRect();
+ }
+}
+
void QQuickTableViewPrivate::unloadEdge(Qt::Edge edge)
{
Q_Q(QQuickTableView);
@@ -2725,7 +2807,7 @@ void QQuickTableViewPrivate::unloadEdge(Qt::Edge edge)
void QQuickTableViewPrivate::loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMode incubationMode)
{
const int edgeIndex = nextVisibleEdgeIndexAroundLoadedTable(edge);
- qCDebug(lcTableViewDelegateLifecycle) << edge << edgeIndex;
+ qCDebug(lcTableViewDelegateLifecycle) << edge << edgeIndex << q_func();
const auto visibleCells = edge & (Qt::LeftEdge | Qt::RightEdge)
? loadedRows.keys() : loadedColumns.keys();
@@ -3226,11 +3308,25 @@ void QQuickTableViewPrivate::syncSyncView()
if (syncHorizontally) {
q->setColumnSpacing(syncView->columnSpacing());
updateContentWidth();
+
+ if (syncView->leftColumn() != q->leftColumn()) {
+ // The left column is no longer the same as the left
+ // column in syncView. This requires a rebuild.
+ scheduledRebuildOptions |= QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftColumn;
+ scheduledRebuildOptions.setFlag(RebuildOption::ViewportOnly);
+ }
}
if (syncVertically) {
q->setRowSpacing(syncView->rowSpacing());
updateContentHeight();
+
+ if (syncView->topRow() != q->topRow()) {
+ // The top row is no longer the same as the top
+ // row in syncView. This requires a rebuild.
+ scheduledRebuildOptions |= QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftRow;
+ scheduledRebuildOptions.setFlag(RebuildOption::ViewportOnly);
+ }
}
if (syncView && loadedItems.isEmpty() && !tableSize.isEmpty()) {
@@ -3456,10 +3552,26 @@ void QQuickTableViewPrivate::setLocalViewportY(qreal contentY)
void QQuickTableViewPrivate::syncViewportRect()
{
- // Sync viewportRect so that it contains the actual geometry of the viewport
+ // Sync viewportRect so that it contains the actual geometry of the viewport.
+ // Since the column (and row) size of a sync child is decided by the column size
+ // of its sync view, the viewport width of a sync view needs to be the maximum of
+ // the sync views width, and its sync childrens width. This to ensure that no sync
+ // child loads a column which is not yet loaded by the sync view, since then the
+ // implicit column size cannot be resolved.
Q_Q(QQuickTableView);
- viewportRect = QRectF(q->contentX(), q->contentY(), q->width(), q->height());
- qCDebug(lcTableViewDelegateLifecycle) << viewportRect;
+
+ qreal w = q->width();
+ qreal h = q->height();
+
+ for (auto syncChild : std::as_const(syncChildren)) {
+ auto syncChild_d = syncChild->d_func();
+ if (syncChild_d->syncHorizontally)
+ w = qMax(w, syncChild->width());
+ if (syncChild_d->syncHorizontally)
+ h = qMax(h, syncChild->height());
+ }
+
+ viewportRect = QRectF(q->contentX(), q->contentY(), w, h);
}
void QQuickTableViewPrivate::syncViewportPosRecursive()
@@ -4154,3 +4266,5 @@ QQuickTableSectionSizeProviderPrivate::~QQuickTableSectionSizeProviderPrivate()
#include "moc_qquicktableview_p.cpp"
QT_END_NAMESPACE
+
+#include "moc_qquicktableview_p_p.cpp"
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h
index 76d5c152da..9893cd97e7 100644
--- a/src/quick/items/qquicktableview_p_p.h
+++ b/src/quick/items/qquicktableview_p_p.h
@@ -71,6 +71,8 @@ Q_DECLARE_LOGGING_CATEGORY(lcTableViewDelegateLifecycle)
static const qreal kDefaultRowHeight = 50;
static const qreal kDefaultColumnWidth = 50;
+static const int kEdgeIndexNotSet = -2;
+static const int kEdgeIndexAtEnd = -3;
class FxTableItem;
class QQuickTableSectionSizeProviderPrivate;
@@ -201,7 +203,9 @@ public:
LoadInitalTable,
VerifyTable,
LayoutTable,
- LoadAndUnloadAfterLayout,
+ CancelOvershootBottomRight,
+ CancelOvershootTopLeft,
+ UpdateContentSize,
PreloadColumns,
PreloadRows,
MovePreloadedItemsToPool,
@@ -288,6 +292,8 @@ public:
// Consider making it public.
bool isTransposed = false;
+ bool warnNoSelectionModel = true;
+
QJSValue rowHeightProvider;
QJSValue columnWidthProvider;
QQuickTableSectionSizeProvider rowHeights;
@@ -396,6 +402,9 @@ public:
int nextVisibleEdgeIndex(Qt::Edge edge, int startIndex);
int nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge);
+ inline bool atTableEnd(Qt::Edge edge) {
+ return nextVisibleEdgeIndexAroundLoadedTable(edge) == kEdgeIndexAtEnd;
+ }
bool allColumnsLoaded();
bool allRowsLoaded();
inline int edgeToArrayIndex(Qt::Edge edge);
@@ -432,8 +441,13 @@ public:
void adjustViewportXAccordingToAlignment();
void adjustViewportYAccordingToAlignment();
+ void cancelOvershootBottomRight();
+ void cancelOvershootTopLeft();
+
void scheduleRebuildTable(QQuickTableViewPrivate::RebuildOptions options);
+ void updateContentSize();
+
QTypeRevision resolveImportVersion();
void createWrapperModel();
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index 0f17af1975..ec1416768e 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -764,11 +764,15 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
const bool pixelSize = font.pixelSize() != -1;
QString layoutText = layout.text();
- int largeFont = pixelSize ? font.pixelSize() : font.pointSize();
- int smallFont = fontSizeMode() != QQuickText::FixedSize
- ? qMin(pixelSize ? minimumPixelSize() : minimumPointSize(), largeFont)
- : largeFont;
- int scaledFontSize = largeFont;
+ const qreal minimumSize = pixelSize
+ ? static_cast<qreal>(minimumPixelSize())
+ : minimumPointSize();
+ qreal largeFont = pixelSize ? font.pixelSize() : font.pointSizeF();
+ qreal smallFont = fontSizeMode() != QQuickText::FixedSize
+ ? qMin<qreal>(minimumSize, largeFont)
+ : largeFont;
+ qreal scaledFontSize = largeFont;
+ const qreal sizeFittingThreshold(0.01);
bool widthChanged = false;
widthExceeded = availableWidth() <= 0 && (singlelineElide || canWrap || horizontalFit);
@@ -797,7 +801,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
if (pixelSize)
scaledFont.setPixelSize(scaledFontSize);
else
- scaledFont.setPointSize(scaledFontSize);
+ scaledFont.setPointSizeF(scaledFontSize);
if (layout.font() != scaledFont)
layout.setFont(scaledFont);
}
@@ -967,7 +971,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
const qreal availWidth = availableWidth();
const qreal availHeight = availableHeight();
- lineWidth = q->widthValid() && availWidth > 0 ? availWidth : naturalWidth;
+ lineWidth = q->widthValid() && q->width() > 0 ? availWidth : naturalWidth;
maxHeight = q->heightValid() ? availHeight : FLT_MAX;
// If the width of the item has changed and it's possible the result of wrapping,
@@ -1061,40 +1065,45 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
if (!horizontalFit && !verticalFit)
break;
+ // Can't find a better fit
+ if (qFuzzyCompare(smallFont, largeFont))
+ break;
+
// Try and find a font size that better fits the dimensions of the element.
if (horizontalFit) {
if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) {
widthExceeded = true;
- largeFont = scaledFontSize - 1;
- if (smallFont > largeFont)
- break;
+ largeFont = scaledFontSize;
+
scaledFontSize = (smallFont + largeFont) / 2;
- if (pixelSize)
- scaledFont.setPixelSize(scaledFontSize);
- else
- scaledFont.setPointSize(scaledFontSize);
+
continue;
} else if (!verticalFit) {
smallFont = scaledFontSize;
- if (smallFont == largeFont)
+
+ // Check to see if the current scaledFontSize is acceptable
+ if ((largeFont - smallFont) < sizeFittingThreshold)
break;
- scaledFontSize = (smallFont + largeFont + 1) / 2;
+
+ scaledFontSize = (smallFont + largeFont) / 2;
}
}
if (verticalFit) {
if (truncateHeight || unelidedRect.height() > maxHeight) {
heightExceeded = true;
- largeFont = scaledFontSize - 1;
- if (smallFont > largeFont)
- break;
+ largeFont = scaledFontSize;
+
scaledFontSize = (smallFont + largeFont) / 2;
} else {
smallFont = scaledFontSize;
- if (smallFont == largeFont)
+
+ // Check to see if the current scaledFontSize is acceptable
+ if ((largeFont - smallFont) < sizeFittingThreshold)
break;
- scaledFontSize = (smallFont + largeFont + 1) / 2;
+
+ scaledFontSize = (smallFont + largeFont) / 2;
}
}
}
@@ -1153,12 +1162,6 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
elideLayout->setFont(layout.font());
elideLayout->setTextOption(layout.textOption());
-#if QT_CONFIG(translation) && QT_CONFIG(qml_debug)
- if (QQmlDebugTranslationService *service
- = QQmlDebugConnector::service<QQmlDebugTranslationService>()) {
- elideText = service->foundElidedText(q, layoutText, elideText);
- }
-#endif //QT_CONFIG(translation)
elideLayout->setText(elideText);
elideLayout->beginLayout();
@@ -2416,21 +2419,23 @@ void QQuickText::geometryChange(const QRectF &newGeometry, const QRectF &oldGeom
goto geomChangeDone;
if (!(widthChanged || widthMaximum) && !d->isLineLaidOutConnected()) { // only height has changed
- if (newGeometry.height() > oldGeometry.height()) {
- if (!d->heightExceeded && !qFuzzyIsNull(oldGeometry.height())) {
- // Height is adequate and growing, and it wasn't 0 previously.
- goto geomChangeDone;
- }
- if (d->lineCount == d->maximumLineCount()) // Reached maximum line and height is growing.
- goto geomChangeDone;
- } else if (newGeometry.height() < oldGeometry.height()) {
- if (d->lineCount < 2 && !verticalScale && newGeometry.height() > 0) // A single line won't be truncated until the text is 0 height.
- goto geomChangeDone;
-
- if (!verticalScale // no scaling, no eliding, and either unwrapped, or no maximum line count.
- && d->elideMode != QQuickText::ElideRight
- && !(d->maximumLineCountValid && d->widthExceeded)) {
- goto geomChangeDone;
+ if (!verticalPositionChanged) {
+ if (newGeometry.height() > oldGeometry.height()) {
+ if (!d->heightExceeded && !qFuzzyIsNull(oldGeometry.height())) {
+ // Height is adequate and growing, and it wasn't 0 previously.
+ goto geomChangeDone;
+ }
+ if (d->lineCount == d->maximumLineCount()) // Reached maximum line and height is growing.
+ goto geomChangeDone;
+ } else if (newGeometry.height() < oldGeometry.height()) {
+ if (d->lineCount < 2 && !verticalScale && newGeometry.height() > 0) // A single line won't be truncated until the text is 0 height.
+ goto geomChangeDone;
+
+ if (!verticalScale // no scaling, no eliding, and either unwrapped, or no maximum line count.
+ && d->elideMode != QQuickText::ElideRight
+ && !(d->maximumLineCountValid && d->widthExceeded)) {
+ goto geomChangeDone;
+ }
}
}
} else if (!heightChanged && widthMaximum) {
@@ -2958,7 +2963,7 @@ void QQuickTextPrivate::processHoverEvent(QHoverEvent *event)
emit q->linkHovered(extra->hoveredLink);
}
}
- event->setAccepted(!link.isEmpty());
+ event->ignore();
}
void QQuickText::hoverEnterEvent(QHoverEvent *event)
@@ -2981,6 +2986,7 @@ void QQuickText::hoverLeaveEvent(QHoverEvent *event)
/*!
\qmlproperty int QtQuick::Text::renderTypeQuality
+ \since 6.0
Override the default rendering type quality for this component. This is a low-level
customization which can be ignored in most cases. It currently only has an effect
diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h
index 304b82b914..13d8579017 100644
--- a/src/quick/items/qquicktext_p.h
+++ b/src/quick/items/qquicktext_p.h
@@ -339,6 +339,8 @@ private:
Q_DECLARE_PRIVATE(QQuickText)
};
+Q_DECLARE_MIXED_ENUM_OPERATORS_SYMMETRIC(int, QQuickText::HAlignment, QQuickText::VAlignment)
+
class QTextLine;
class QQuickTextLine : public QObject
{
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index 32a27661ea..1412b68ceb 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -1203,7 +1203,15 @@ void QQuickTextEdit::setCursorVisible(bool on)
/*!
\qmlproperty int QtQuick::TextEdit::cursorPosition
- The position of the cursor in the TextEdit.
+ The position of the cursor in the TextEdit. The cursor is positioned between
+ characters.
+
+ \note The \e characters in this case refer to the string of \l QChar objects,
+ therefore 16-bit Unicode characters, and the position is considered an index
+ into this string. This does not necessarily correspond to individual graphemes
+ in the writing system, as a single grapheme may be represented by multiple
+ Unicode characters, such as in the case of surrogate pairs, linguistic
+ ligatures or diacritics.
*/
int QQuickTextEdit::cursorPosition() const
{
@@ -2959,6 +2967,7 @@ void QQuickTextEdit::hoverEnterEvent(QHoverEvent *event)
Q_D(QQuickTextEdit);
if (d->isLinkHoveredConnected())
d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
+ event->ignore();
}
void QQuickTextEdit::hoverMoveEvent(QHoverEvent *event)
@@ -2966,6 +2975,7 @@ void QQuickTextEdit::hoverMoveEvent(QHoverEvent *event)
Q_D(QQuickTextEdit);
if (d->isLinkHoveredConnected())
d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
+ event->ignore();
}
void QQuickTextEdit::hoverLeaveEvent(QHoverEvent *event)
@@ -2973,6 +2983,7 @@ void QQuickTextEdit::hoverLeaveEvent(QHoverEvent *event)
Q_D(QQuickTextEdit);
if (d->isLinkHoveredConnected())
d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
+ event->ignore();
}
/*!
diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h
index 41245f904e..7b43a847c1 100644
--- a/src/quick/items/qquicktextedit_p.h
+++ b/src/quick/items/qquicktextedit_p.h
@@ -422,6 +422,8 @@ private:
Q_DECLARE_PRIVATE(QQuickTextEdit)
};
+Q_DECLARE_MIXED_ENUM_OPERATORS_SYMMETRIC(int, QQuickTextEdit::HAlignment, QQuickTextEdit::VAlignment)
+
QT_END_NAMESPACE
QML_DECLARE_TYPE(QQuickTextEdit)
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index e5922df623..c08f357df8 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -517,8 +517,16 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color)
}
/*!
- \qmlproperty enumeration QtQuick::TextInput::horizontalAlignment
\qmlproperty enumeration QtQuick::TextInput::effectiveHorizontalAlignment
+ \readonly
+
+ When using the attached property LayoutMirroring::enabled to mirror application
+ layouts, the horizontal alignment of text will also be mirrored. However, the property
+ \l horizontalAlignment will remain unchanged. To query the effective horizontal alignment
+ of TextInput, use the read-only property \c effectiveHorizontalAlignment.
+*/
+/*!
+ \qmlproperty enumeration QtQuick::TextInput::horizontalAlignment
\qmlproperty enumeration QtQuick::TextInput::verticalAlignment
Sets the horizontal alignment of the text within the TextInput item's
@@ -541,7 +549,7 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color)
When using the attached property LayoutMirroring::enabled to mirror application
layouts, the horizontal alignment of text will also be mirrored. However, the property
\c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
- of TextInput, use the read-only property \c effectiveHorizontalAlignment.
+ of TextInput, use the read-only property \l effectiveHorizontalAlignment.
*/
QQuickTextInput::HAlignment QQuickTextInput::hAlign() const
{
@@ -830,7 +838,20 @@ void QQuickTextInput::setCursorVisible(bool on)
/*!
\qmlproperty int QtQuick::TextInput::cursorPosition
- The position of the cursor in the TextInput.
+ The position of the cursor in the TextInput. The cursor is positioned between
+ characters.
+
+ \note The \e characters in this case refer to the string of \l QChar objects,
+ therefore 16-bit Unicode characters, and the position is considered an index
+ into this string. This does not necessarily correspond to individual graphemes
+ in the writing system, as a single grapheme may be represented by multiple
+ Unicode characters, such as in the case of surrogate pairs, linguistic
+ ligatures or diacritics.
+
+ \l displayText is different if echoMode is set to \l TextInput.Password: then
+ each passwordMaskCharacter is a "narrow" character
+ (the cursorPosition always moves by 1), even if the text in the TextInput is not.
+
*/
int QQuickTextInput::cursorPosition() const
{
@@ -848,6 +869,7 @@ void QQuickTextInput::setCursorPosition(int cp)
/*!
\qmlproperty rectangle QtQuick::TextInput::cursorRectangle
+ \readonly
The rectangle where the standard text cursor is rendered within the text input. Read only.
@@ -889,6 +911,7 @@ QRectF QQuickTextInput::cursorRectangle() const
This property is read-only. To change the selection, use select(start,end),
selectAll(), or selectWord().
+ \readonly
\sa selectionEnd, cursorPosition, selectedText
*/
int QQuickTextInput::selectionStart() const
@@ -904,6 +927,7 @@ int QQuickTextInput::selectionStart() const
This property is read-only. To change the selection, use select(start,end),
selectAll(), or selectWord().
+ \readonly
\sa selectionStart, cursorPosition, selectedText
*/
int QQuickTextInput::selectionEnd() const
@@ -934,6 +958,7 @@ void QQuickTextInput::select(int start, int end)
/*!
\qmlproperty string QtQuick::TextInput::selectedText
+ \readonly
This read-only property provides the text currently selected in the
text input.
@@ -1151,6 +1176,7 @@ void QQuickTextInput::setInputMask(const QString &im)
/*!
\qmlproperty bool QtQuick::TextInput::acceptableInput
+ \readonly
This property is always true unless a validator or input mask has been set.
If a validator or input mask has been set, this property will only be true
@@ -2001,6 +2027,8 @@ QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property, const
if (argument.isValid())
return QVariant(QStringView{d->m_text}.left(d->m_cursor).right(argument.toInt()).toString());
return QVariant(d->m_text.left(d->m_cursor));
+ case Qt::ImReadOnly:
+ return QVariant(d->m_readOnly);
default:
return QQuickItem::inputMethodQuery(property);
}
@@ -2104,7 +2132,7 @@ void QQuickTextInput::undo()
{
Q_D(QQuickTextInput);
if (!d->m_readOnly) {
- d->resetInputMethod();
+ d->cancelInput();
d->internalUndo();
d->finishChange(-1, true);
}
@@ -2120,7 +2148,7 @@ void QQuickTextInput::redo()
{
Q_D(QQuickTextInput);
if (!d->m_readOnly) {
- d->resetInputMethod();
+ d->cancelInput();
d->internalRedo();
d->finishChange();
}
@@ -2458,6 +2486,7 @@ void QQuickTextInput::setPersistentSelection(bool on)
/*!
\qmlproperty bool QtQuick::TextInput::canPaste
+ \readonly
Returns true if the TextInput is writable and the content of the clipboard is
suitable for pasting into the TextInput.
@@ -2479,6 +2508,7 @@ bool QQuickTextInput::canPaste() const
/*!
\qmlproperty bool QtQuick::TextInput::canUndo
+ \readonly
Returns true if the TextInput is writable and there are previous operations
that can be undone.
@@ -2492,6 +2522,7 @@ bool QQuickTextInput::canUndo() const
/*!
\qmlproperty bool QtQuick::TextInput::canRedo
+ \readonly
Returns true if the TextInput is writable and there are \l {undo}{undone}
operations that can be redone.
@@ -2505,6 +2536,7 @@ bool QQuickTextInput::canRedo() const
/*!
\qmlproperty real QtQuick::TextInput::contentWidth
+ \readonly
Returns the width of the text, including the width past the width
which is covered due to insufficient wrapping if \l wrapMode is set.
@@ -2518,6 +2550,7 @@ qreal QQuickTextInput::contentWidth() const
/*!
\qmlproperty real QtQuick::TextInput::contentHeight
+ \readonly
Returns the height of the text, including the height past the height
that is covered if the text does not fit within the set height.
@@ -2681,7 +2714,7 @@ void QQuickTextInput::focusOutEvent(QFocusEvent *event)
/*!
\qmlproperty bool QtQuick::TextInput::inputMethodComposing
-
+ \readonly
This property holds whether the TextInput has partial text input from an
input method.
@@ -2747,11 +2780,13 @@ void QQuickTextInputPrivate::init()
m_inputControl = new QInputControl(QInputControl::LineEdit, q);
}
-void QQuickTextInputPrivate::resetInputMethod()
+void QQuickTextInputPrivate::cancelInput()
{
+#if QT_CONFIG(im)
Q_Q(QQuickTextInput);
if (!m_readOnly && q->hasActiveFocus() && qGuiApp)
- QGuiApplication::inputMethod()->reset();
+ cancelPreedit();
+#endif // im
}
void QQuickTextInput::updateCursorRectangle(bool scroll)
@@ -3459,7 +3494,12 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
for (int i = 0; i < event->attributes().size(); ++i) {
const QInputMethodEvent::Attribute &a = event->attributes().at(i);
if (a.type == QInputMethodEvent::Selection) {
- m_cursor = qBound(0, a.start + a.length, m_text.length());
+ // If we already called internalInsert(), the cursor position will
+ // already be adjusted correctly. The attribute.start does
+ // not seem to take the mask into account, so it will reset cursor
+ // to an invalid position in such case.
+ if (!cursorPositionChanged)
+ m_cursor = qBound(0, a.start + a.length, m_text.length());
if (a.length) {
m_selstart = qMax(0, qMin(a.start, m_text.length()));
m_selend = m_cursor;
@@ -4696,7 +4736,7 @@ void QQuickTextInput::ensureVisible(int position)
void QQuickTextInput::clear()
{
Q_D(QQuickTextInput);
- d->resetInputMethod();
+ d->cancelInput();
d->clear();
}
diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h
index b4561556aa..1fecd7f9df 100644
--- a/src/quick/items/qquicktextinput_p_p.h
+++ b/src/quick/items/qquicktextinput_p_p.h
@@ -173,7 +173,7 @@ public:
}
void init();
- void resetInputMethod();
+ void cancelInput();
void startCreatingCursor();
void ensureVisible(int position, int preeditCursor = 0, int preeditLength = 0);
void updateHorizontalScroll();
diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp
index b3401200c8..763607f730 100644
--- a/src/quick/items/qquicktextnodeengine.cpp
+++ b/src/quick/items/qquicktextnodeengine.cpp
@@ -209,10 +209,7 @@ void QQuickTextNodeEngine::addTextDecorations(const QVarLengthArray<TextDecorati
{
QRectF &rect = textDecoration.rect;
- rect.setY(qRound(rect.y()
- + m_currentLine.ascent()
- + (m_currentLine.leadingIncluded() ? m_currentLine.leading() : qreal(0.0f))
- + offset));
+ rect.setY(qRound(rect.y() + m_currentLine.ascent() + offset));
rect.setHeight(thickness);
}
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 314223a838..19b508cecb 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -483,18 +483,14 @@ void forceUpdate(QQuickItem *item)
forceUpdate(items.at(i));
}
-void QQuickWindowRenderTarget::reset(QRhi *rhi, QSGRenderer *renderer)
+void QQuickWindowRenderTarget::reset(QRhi *rhi)
{
- if (rhi) {
- if (renderer)
- renderer->invalidatePipelineCacheDependency(rpDesc);
- if (owns) {
- delete renderTarget;
- delete rpDesc;
- delete texture;
- delete renderBuffer;
- delete depthStencil;
- }
+ if (rhi && owns) {
+ delete renderTarget;
+ delete rpDesc;
+ delete texture;
+ delete renderBuffer;
+ delete depthStencil;
}
renderTarget = nullptr;
@@ -514,7 +510,7 @@ void QQuickWindowPrivate::ensureCustomRenderTarget()
redirect.renderTargetDirty = false;
- redirect.rt.reset(rhi, renderer);
+ redirect.rt.reset(rhi);
// a default constructed QQuickRenderTarget means no redirection
if (customRenderTarget.isNull())
@@ -723,7 +719,7 @@ QQuickWindowPrivate::QQuickWindowPrivate()
QQuickWindowPrivate::~QQuickWindowPrivate()
{
inDestructor = true;
- redirect.rt.reset(rhi, renderer);
+ redirect.rt.reset(rhi);
if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
service->removeWindow(q_func());
deliveryAgent = nullptr;
@@ -884,14 +880,6 @@ void QQuickWindowPrivate::cleanup(QSGNode *n)
The Window object creates a new top-level window for a Qt Quick scene. It automatically sets up the
window for use with \c {QtQuick} graphical types.
- To use this type, you will need to import the module with the following line:
- \code
- import QtQuick
- \endcode
-
- Omitting this import will allow you to have a QML environment without
- access to window system features.
-
A Window can be declared inside an Item or inside another Window; in that
case the inner Window will automatically become "transient for" the outer
Window: that is, most platforms will show it centered upon the outer window
@@ -907,9 +895,23 @@ void QQuickWindowPrivate::cleanup(QSGNode *n)
When the user attempts to close a window, the \l closing signal will be
emitted. You can force the window to stay open (for example to prompt the
- user to save changes) by writing an \c onClosing handler and setting
- \c {close.accepted = false}.
+ user to save changes) by writing an \c onClosing handler that sets
+ \c {close.accepted = false} unless it's safe to close the window (for example,
+ because there are no more unsaved changes).
+
+ \code
+ onClosing: (close) => {
+ if (document.changed) {
+ close.accepted = false
+ confirmExitPopup.open()
+ }
+ }
+
+ // The confirmExitPopup allows user to save or discard the document,
+ // or to cancel the closing.
+ \endcode
*/
+
/*!
\class QQuickWindow
\since 5.0
@@ -1094,7 +1096,11 @@ QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QWindow *parent)
}
/*!
- \internal
+ Constructs a window for displaying a QML scene, whose rendering will
+ be controlled by the \a control object.
+ Please refer to QQuickRenderControl's documentation for more information.
+
+ \since 5.4
*/
QQuickWindow::QQuickWindow(QQuickRenderControl *control)
: QWindow(*(new QQuickWindowPrivate), nullptr)
@@ -1318,6 +1324,8 @@ QQuickItem *QQuickWindow::contentItem() const
\brief The item which currently has active focus or \c null if there is
no item with active focus.
+
+ \sa QQuickItem::forceActiveFocus(), {Keyboard Focus in Qt Quick}
*/
QQuickItem *QQuickWindow::activeFocusItem() const
{
@@ -1350,6 +1358,38 @@ bool QQuickWindow::event(QEvent *e)
QQuickDeliveryAgent *da = d->deliveryAgent;
if (e->isPointerEvent()) {
/*
+ We can't bypass the virtual functions like mousePressEvent() tabletEvent() etc.,
+ for the sake of code that subclasses QQuickWindow and overrides them, even though
+ we no longer need them as entry points for Qt Quick event delivery.
+ So dispatch to them now, ahead of normal delivery, and stop them from calling
+ back into this function if they were called from here (avoid recursion).
+ It could also be that user code expects them to work as entry points, too;
+ in that case, windowEventDispatch _won't_ be set, so the event comes here and
+ we'll dispatch it further below.
+ */
+ if (d->windowEventDispatch)
+ return false;
+ {
+ const bool wasAccepted = e->isAccepted();
+ QBoolBlocker windowEventDispatchGuard(d->windowEventDispatch, true);
+ qCDebug(lcPtr) << "dispatching to window functions in case of override" << e;
+ QWindow::event(e);
+ if (e->isAccepted() && !wasAccepted)
+ return true;
+ }
+ /*
+ QQuickWindow does not override touchEvent(). If the application has a subclass
+ of QQuickWindow which allows the event to remain accepted, it means they want
+ to stop propagation here, so return early (below). But otherwise we will call
+ QWindow::touchEvent(), which will ignore(); in that case, we need to continue
+ with the usual delivery below, so we need to undo the ignore().
+ */
+ auto pe = static_cast<QPointerEvent *>(e);
+ if (QQuickDeliveryAgentPrivate::isTouchEvent(pe))
+ e->accept();
+ // end of dispatch to user-overridden virtual window functions
+
+ /*
When delivering update and release events to existing grabbers,
use the subscene delivery agent, if any. A possible scenario:
1) Two touchpoints pressed on the main window: QQuickWindowPrivate::deliveryAgent delivers to QQuick3DViewport,
@@ -1362,7 +1402,6 @@ bool QQuickWindow::event(QEvent *e)
With single-point events (mouse, or only one finger) it's simplified: there can only be one subscene of interest;
for (pt : pe->points()) would only iterate once, so we might as well skip that logic.
*/
- auto pe = static_cast<QPointerEvent *>(e);
if (pe->pointCount()) {
if (QQuickDeliveryAgentPrivate::subsceneAgentsExist) {
bool ret = false;
@@ -1533,13 +1572,18 @@ bool QQuickWindow::event(QEvent *e)
else if (e->type() == QEvent::Type(QQuickWindowPrivate::TriggerContextCreationFailure))
d->windowManager->handleContextCreationFailure(this);
- return QWindow::event(e);
+ if (e->isPointerEvent())
+ return true;
+ else
+ return QWindow::event(e);
}
/*! \reimp */
void QQuickWindow::keyPressEvent(QKeyEvent *e)
{
Q_D(QQuickWindow);
+ if (d->windowEventDispatch)
+ return;
auto da = d->deliveryAgentPrivate();
Q_ASSERT(da);
da->deliverKeyEvent(e);
@@ -1549,6 +1593,8 @@ void QQuickWindow::keyPressEvent(QKeyEvent *e)
void QQuickWindow::keyReleaseEvent(QKeyEvent *e)
{
Q_D(QQuickWindow);
+ if (d->windowEventDispatch)
+ return;
auto da = d->deliveryAgentPrivate();
Q_ASSERT(da);
da->deliverKeyEvent(e);
@@ -1559,6 +1605,8 @@ void QQuickWindow::keyReleaseEvent(QKeyEvent *e)
void QQuickWindow::wheelEvent(QWheelEvent *event)
{
Q_D(QQuickWindow);
+ if (d->windowEventDispatch)
+ return;
auto da = d->deliveryAgentPrivate();
Q_ASSERT(da);
da->deliverSinglePointEventUntilAccepted(event);
@@ -1570,6 +1618,8 @@ void QQuickWindow::wheelEvent(QWheelEvent *event)
void QQuickWindow::tabletEvent(QTabletEvent *event)
{
Q_D(QQuickWindow);
+ if (d->windowEventDispatch)
+ return;
auto da = d->deliveryAgentPrivate();
Q_ASSERT(da);
da->deliverPointerEvent(event);
@@ -1580,6 +1630,8 @@ void QQuickWindow::tabletEvent(QTabletEvent *event)
void QQuickWindow::mousePressEvent(QMouseEvent *event)
{
Q_D(QQuickWindow);
+ if (d->windowEventDispatch)
+ return;
auto da = d->deliveryAgentPrivate();
Q_ASSERT(da);
da->handleMouseEvent(event);
@@ -1588,6 +1640,8 @@ void QQuickWindow::mousePressEvent(QMouseEvent *event)
void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
{
Q_D(QQuickWindow);
+ if (d->windowEventDispatch)
+ return;
auto da = d->deliveryAgentPrivate();
Q_ASSERT(da);
da->handleMouseEvent(event);
@@ -1596,6 +1650,8 @@ void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event)
{
Q_D(QQuickWindow);
+ if (d->windowEventDispatch)
+ return;
auto da = d->deliveryAgentPrivate();
Q_ASSERT(da);
da->handleMouseEvent(event);
@@ -1604,6 +1660,8 @@ void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event)
void QQuickWindow::mouseReleaseEvent(QMouseEvent *event)
{
Q_D(QQuickWindow);
+ if (d->windowEventDispatch)
+ return;
auto da = d->deliveryAgentPrivate();
Q_ASSERT(da);
da->handleMouseEvent(event);
@@ -1672,6 +1730,12 @@ QPair<QQuickItem*, QQuickPointerHandler*> QQuickWindowPrivate::findCursorItemAnd
}
#endif
+void QQuickWindowPrivate::clearFocusObject()
+{
+ if (auto da = deliveryAgentPrivate())
+ da->clearFocusObject();
+}
+
/*!
\qmlproperty list<Object> Window::data
\qmldefault
@@ -1787,7 +1851,7 @@ void QQuickWindowPrivate::cleanupNodesOnShutdown(QQuickItem *item)
p->dirty(QQuickItemPrivate::Window);
}
- // Qt 6: Make invalidateSceneGraph a virtual member of QQuickItem
+ // Qt 7: Make invalidateSceneGraph a virtual member of QQuickItem
if (p->flags & QQuickItem::ItemHasContents) {
const QMetaObject *mo = item->metaObject();
int index = mo->indexOfSlot("invalidateSceneGraph()");
@@ -2000,9 +2064,6 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
// desired list is shorter.
QSGNode *groupNode = itemPriv->childContainerNode();
QSGNode *currentNode = groupNode->firstChild();
- int added = 0;
- int removed = 0;
- int replaced = 0;
QSGNode *desiredNode = nullptr;
while (currentNode && (desiredNode = fetchNextNode(itemPriv, ii, fetchedPaintNode))) {
@@ -2015,7 +2076,6 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
desiredNode->parent()->removeChildNode(desiredNode);
groupNode->insertChildNodeAfter(desiredNode, currentNode);
groupNode->removeChildNode(currentNode);
- replaced++;
// since we just replaced currentNode, we also need to reset
// the pointer.
@@ -2034,7 +2094,6 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
if (desiredNode->parent())
desiredNode->parent()->removeChildNode(desiredNode);
groupNode->appendChildNode(desiredNode);
- added++;
}
} else if (currentNode) {
// on the other hand, if we processed less than our current node
@@ -2044,7 +2103,6 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
QSGNode *node = currentNode->nextSibling();
groupNode->removeChildNode(currentNode);
currentNode = node;
- removed++;
}
}
}
@@ -2490,7 +2548,7 @@ QImage QQuickWindow::grabWindow()
{
Q_D(QQuickWindow);
- if (!isVisible() && !d->renderControl) {
+ if (!d->isRenderable() && !d->renderControl) {
// backends like software can grab regardless of the window state
if (d->windowManager && (d->windowManager->flags() & QSGRenderLoop::SupportsGrabWithoutExpose))
return d->windowManager->grab(this);
@@ -3780,7 +3838,7 @@ QSGRendererInterface *QQuickWindow::rendererInterface() const
graphics API based on the platform and other conditions, set \a api to
QSGRendererInterface::Unknown.
- \since 5.8
+ \since 6.0
*/
void QQuickWindow::setGraphicsApi(QSGRendererInterface::GraphicsApi api)
{
@@ -4082,7 +4140,7 @@ void QQuickWindow::setTextRenderType(QQuickWindow::TextRenderType renderType)
/*!
\since 6.0
- \qmlproperty QQuickPalette Window::palette
+ \qmlproperty Palette Window::palette
This property holds the palette currently set for the window.
@@ -4094,7 +4152,7 @@ void QQuickWindow::setTextRenderType(QQuickWindow::TextRenderType renderType)
property on the window's palette, that property propagates to all child controls in the window,
overriding any system defaults for that property.
- \sa Item::palette, Popup::palette, QQuickColorGroup
+ \sa Item::palette, Popup::palette, ColorGroup, SystemPalette
//! internal \sa QQuickAbstractPaletteProvider, QQuickPalette
*/
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index 7342424251..3b442499bb 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -110,7 +110,7 @@ public Q_SLOTS:
class QQuickWindowRenderTarget
{
public:
- void reset(QRhi *rhi, QSGRenderer *renderer);
+ void reset(QRhi *rhi);
QRhiRenderTarget *renderTarget = nullptr;
QRhiRenderPassDescriptor *rpDesc = nullptr;
QRhiTexture *texture = nullptr;
@@ -161,6 +161,8 @@ public:
QPair<QQuickItem*, QQuickPointerHandler*> findCursorItemAndHandler(QQuickItem *item, const QPointF &scenePos) const;
#endif
+ void clearFocusObject() override;
+
void dirtyItem(QQuickItem *);
void cleanup(QSGNode *);
@@ -249,9 +251,18 @@ public:
void clearFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason)
{ deliveryAgentPrivate()->clearFocusInScope(scope, item, reason); }
void handleTouchEvent(QTouchEvent *e)
- { deliveryAgentPrivate()->handleTouchEvent(e); }
+ {
+ // setup currentEventDeliveryAgent like in QQuickDeliveryAgent::event
+ QQuickDeliveryAgentPrivate::currentEventDeliveryAgent = deliveryAgentPrivate()->q_func();
+ deliveryAgentPrivate()->handleTouchEvent(e);
+ QQuickDeliveryAgentPrivate::currentEventDeliveryAgent = nullptr;
+ }
void handleMouseEvent(QMouseEvent *e)
- { deliveryAgentPrivate()->handleMouseEvent(e); }
+ {
+ QQuickDeliveryAgentPrivate::currentEventDeliveryAgent = deliveryAgentPrivate()->q_func();
+ deliveryAgentPrivate()->handleMouseEvent(e);
+ QQuickDeliveryAgentPrivate::currentEventDeliveryAgent = nullptr;
+ }
// ^^^ currently in use in Controls 2; TODO remove
// data property
@@ -287,6 +298,7 @@ public:
uint hasActiveSwapchain : 1;
uint hasRenderableSwapchain : 1;
uint swapchainJustBecameRenderable : 1;
+ bool windowEventDispatch = false;
private:
static void cleanupNodesOnShutdown(QQuickItem *);
diff --git a/src/quick/items/qquickwindowmodule_p.h b/src/quick/items/qquickwindowmodule_p.h
index 7177469b55..c1cd48f964 100644
--- a/src/quick/items/qquickwindowmodule_p.h
+++ b/src/quick/items/qquickwindowmodule_p.h
@@ -74,7 +74,8 @@ class Q_QUICK_PRIVATE_EXPORT QQuickWindowQmlImpl : public QQuickWindow, public Q
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
- Q_PROPERTY(Visibility visibility READ visibility WRITE setVisibility NOTIFY visibilityChanged)
+ Q_PROPERTY(QWindow::Visibility visibility READ visibility WRITE setVisibility NOTIFY
+ visibilityChanged)
Q_PROPERTY(QObject *screen READ screen WRITE setScreen NOTIFY screenChanged REVISION(2, 3))
QML_ATTACHED(QQuickWindowAttached)
QML_NAMED_ELEMENT(Window)
@@ -84,7 +85,7 @@ public:
QQuickWindowQmlImpl(QWindow *parent = nullptr);
void setVisible(bool visible);
- void setVisibility(Visibility visibility);
+ void setVisibility(QWindow::Visibility visibility);
QObject *screen() const;
void setScreen(QObject *screen);
diff --git a/src/quick/qtquickglobal_p.h b/src/quick/qtquickglobal_p.h
index 797d9f169a..0ef394e739 100644
--- a/src/quick/qtquickglobal_p.h
+++ b/src/quick/qtquickglobal_p.h
@@ -72,6 +72,36 @@ Q_DECLARE_LOGGING_CATEGORY(lcMouse)
Q_DECLARE_LOGGING_CATEGORY(lcFocus)
Q_DECLARE_LOGGING_CATEGORY(lcDirty)
+/*
+ This is needed for QuickTestUtils. Q_AUTOTEST_EXPORT checks QT_BUILDING_QT
+ (amongst others) to see if it should export symbols. Until QuickTestUtils
+ was introduced, this was enough, as there weren't any intermediate test
+ helper libraries that used a Qt library and were in turn used by tests.
+
+ Taking QQuickItemViewPrivate as an example: previously it was using
+ Q_AUTOTEST_EXPORT. Since QuickTestUtils is a Qt library (albeit a private
+ one), QT_BUILDING_QT was true and so Q_AUTOTEST_EXPORT evaluated to an
+ export. However, QQuickItemViewPrivate was already exported by the Quick
+ library, so we would get errors like this:
+
+ Qt6Quickd.lib(Qt6Quickd.dll) : error LNK2005: "public: static class
+ QQuickItemViewPrivate * __cdecl QQuickItemViewPrivate::get(class QQuickItemView *)"
+ (?get@QQuickItemViewPrivate@@SAPEAV1@PEAVQQuickItemView@@@Z) already defined
+ in Qt6QuickTestUtilsd.lib(viewtestutils.cpp.obj)
+
+ So, to account for the special case of QuickTestUtils, we need to be more
+ specific about which part of Qt we're building; instead of checking if we're
+ building any Qt library at all, check if we're building the Quick library,
+ and only then export.
+*/
+#if defined(QT_BUILD_INTERNAL) && defined(QT_BUILD_QUICK_LIB) && defined(QT_SHARED)
+# define Q_QUICK_AUTOTEST_EXPORT Q_DECL_EXPORT
+#elif defined(QT_BUILD_INTERNAL) && defined(QT_SHARED)
+# define Q_QUICK_AUTOTEST_EXPORT Q_DECL_IMPORT
+#else
+# define Q_QUICK_AUTOTEST_EXPORT
+#endif
+
QT_END_NAMESPACE
#endif // QTQUICKGLOBAL_P_H
diff --git a/src/quick/qtquickplugin.cpp b/src/quick/qtquickplugin.cpp
index e4ceee15a9..eeca8eac5e 100644
--- a/src/quick/qtquickplugin.cpp
+++ b/src/quick/qtquickplugin.cpp
@@ -42,6 +42,9 @@
QT_BEGIN_NAMESPACE
+Q_GHS_KEEP_REFERENCE(qml_register_types_QtQuick);
+Q_GHS_KEEP_REFERENCE(QQuick_initializeModule);
+
class QtQuick2Plugin : public QQmlEngineExtensionPlugin
{
Q_OBJECT
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp
index e76baaa632..30b4d7aa47 100644
--- a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp
+++ b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp
@@ -173,3 +173,5 @@ QSGTexture *Texture::removedFromAtlas(QRhiResourceUpdateBatch *) const
}
QT_END_NAMESPACE
+
+#include "moc_qsgcompressedatlastexture_p.cpp"
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
index 46dc6c5507..d5d22f3677 100644
--- a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
+++ b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
@@ -377,19 +377,20 @@ void QSGCompressedTexture::commitTextureOperations(QRhi *rhi, QRhiResourceUpdate
return;
}
- QRhiTexture::Flags texFlags;
- if (fmt.isSRGB)
- texFlags |= QRhiTexture::sRGB;
+ if (!m_texture) {
+ QRhiTexture::Flags texFlags;
+ if (fmt.isSRGB)
+ texFlags |= QRhiTexture::sRGB;
- if (!rhi->isTextureFormatSupported(fmt.rhiFormat, texFlags)) {
- qWarning("Unsupported compressed format 0x%x", m_textureData.glInternalFormat());
- return;
- }
+ if (!rhi->isTextureFormatSupported(fmt.rhiFormat, texFlags)) {
+ qCDebug(QSG_LOG_TEXTUREIO, "Compressed texture format possibly unsupported: 0x%x",
+ m_textureData.glInternalFormat());
+ }
- if (!m_texture) {
m_texture = rhi->newTexture(fmt.rhiFormat, m_size, 1, texFlags);
if (!m_texture->create()) {
- qWarning("Failed to create QRhiTexture for compressed data");
+ qWarning("Failed to create QRhiTexture for compressed data with format 0x%x",
+ m_textureData.glInternalFormat());
delete m_texture;
m_texture = nullptr;
return;
@@ -466,3 +467,5 @@ QSize QSGCompressedTextureFactory::textureSize() const
}
QT_END_NAMESPACE
+
+#include "moc_qsgcompressedtexture_p.cpp"
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp b/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
index 6f14d5abff..05a952bc9f 100644
--- a/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp
@@ -377,3 +377,5 @@ void QSGAbstractRenderer::renderSceneInline()
}
QT_END_NAMESPACE
+
+#include "moc_qsgabstractrenderer_p.cpp"
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index 6d6b2aca23..c6877369c0 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -299,23 +299,23 @@ void ShaderManager::invalidated()
qDeleteAll(rewrittenShaders);
rewrittenShaders.clear();
- qDeleteAll(srbCache);
- srbCache.clear();
-
qDeleteAll(pipelineCache);
pipelineCache.clear();
+
+ qDeleteAll(srbPool);
+ srbPool.clear();
}
void ShaderManager::clearCachedRendererData()
{
- for (ShaderManager::Shader *sms : stockShaders) {
+ for (ShaderManager::Shader *sms : qAsConst(stockShaders)) {
QSGMaterialShader *s = sms->programRhi.program;
if (s) {
QSGMaterialShaderPrivate *sd = QSGMaterialShaderPrivate::get(s);
sd->clearCachedRendererData();
}
}
- for (ShaderManager::Shader *sms : rewrittenShaders) {
+ for (ShaderManager::Shader *sms : qAsConst(rewrittenShaders)) {
QSGMaterialShader *s = sms->programRhi.program;
if (s) {
QSGMaterialShaderPrivate *sd = QSGMaterialShaderPrivate::get(s);
@@ -324,24 +324,6 @@ void ShaderManager::clearCachedRendererData()
}
}
-QRhiShaderResourceBindings *ShaderManager::srb(const ShaderResourceBindingList &bindings)
-{
- auto it = srbCache.constFind(bindings);
- if (it != srbCache.constEnd())
- return *it;
-
- QRhiShaderResourceBindings *srb = context->rhi()->newShaderResourceBindings();
- srb->setBindings(bindings.cbegin(), bindings.cend());
- if (srb->create()) {
- srbCache.insert(bindings, srb);
- } else {
- qWarning("Failed to build srb");
- delete srb;
- srb = nullptr;
- }
- return srb;
-}
-
void qsg_dumpShadowRoots(BatchRootInfo *i, int indent)
{
static int extraIndent = 0;
@@ -918,7 +900,7 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx, QSGRendererInterface::RenderMod
// The shader manager is shared between renderers (think for example Item
// layers that create a new Renderer each) with the same rendercontext (and
// so same QRhi).
- m_shaderManager = ctx->findChild<ShaderManager *>(QStringLiteral("__qt_ShaderManager"), Qt::FindDirectChildrenOnly);
+ m_shaderManager = ctx->findChild<ShaderManager *>(QString(), Qt::FindDirectChildrenOnly);
if (!m_shaderManager) {
m_shaderManager = new ShaderManager(ctx);
m_shaderManager->setObjectName(QStringLiteral("__qt_ShaderManager"));
@@ -928,17 +910,17 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx, QSGRendererInterface::RenderMod
m_batchNodeThreshold = qt_sg_envInt("QSG_RENDERER_BATCH_NODE_THRESHOLD", 64);
m_batchVertexThreshold = qt_sg_envInt("QSG_RENDERER_BATCH_VERTEX_THRESHOLD", 1024);
+ m_srbPoolThreshold = qt_sg_envInt("QSG_RENDERER_SRB_POOL_THRESHOLD", 1024);
if (Q_UNLIKELY(debug_build() || debug_render())) {
- qDebug("Batch thresholds: nodes: %d vertices: %d",
- m_batchNodeThreshold, m_batchVertexThreshold);
+ qDebug("Batch thresholds: nodes: %d vertices: %d Srb pool threshold: %d",
+ m_batchNodeThreshold, m_batchVertexThreshold, m_srbPoolThreshold);
}
}
static void qsg_wipeBuffer(Buffer *buffer)
{
- if (buffer->buf)
- delete buffer->buf;
+ delete buffer->buf;
// The free here is ok because we're in one of two situations.
// 1. We're using the upload pool in which case unmap will have set the
@@ -961,11 +943,6 @@ static void qsg_wipeBatch(Batch *batch, bool separateIndexBuffer)
Renderer::~Renderer()
{
if (m_rhi) {
- // If setExternalRenderPassDescriptor() was called, we have to
- // aggressively invalidate to prevent an object, the lifetime of which
- // we have no control over, staying in the (per-window) caches.
- invalidatePipelineCacheDependency(m_external_rp_desc);
-
// Clean up batches and buffers
const bool separateIndexBuffer = m_context->separateIndexBuffer();
for (int i = 0; i < m_opaqueBatches.size(); ++i)
@@ -976,17 +953,18 @@ Renderer::~Renderer()
qsg_wipeBatch(m_batchPool.at(i), separateIndexBuffer);
}
- for (Node *n : qAsConst(m_nodes))
+ for (Node *n : qAsConst(m_nodes)) {
+ if (n->type() == QSGNode::GeometryNodeType) {
+ Element *e = n->element();
+ if (!e->removed)
+ m_elementsToDelete.add(e);
+ }
m_nodeAllocator.release(n);
+ }
// Remaining elements...
- for (int i=0; i<m_elementsToDelete.size(); ++i) {
- Element *e = m_elementsToDelete.at(i);
- if (e->isRenderNode)
- delete static_cast<RenderNodeElement *>(e);
- else
- m_elementAllocator.release(e);
- }
+ for (int i=0; i<m_elementsToDelete.size(); ++i)
+ releaseElement(m_elementsToDelete.at(i), true);
destroyGraphicsResources();
@@ -1017,6 +995,9 @@ void Renderer::releaseCachedResources()
m_dummyTexture = nullptr;
m_rhi->releaseCachedResources();
+
+ m_vertexUploadPool.resize(0);
+ m_indexUploadPool.resize(0);
}
void Renderer::invalidateAndRecycleBatch(Batch *b)
@@ -1055,8 +1036,11 @@ void Renderer::unmap(Buffer *buffer, bool isIndexBuf)
buffer->buf = m_rhi->newBuffer(QRhiBuffer::Immutable,
isIndexBuf ? QRhiBuffer::IndexBuffer : QRhiBuffer::VertexBuffer,
buffer->size);
- if (!buffer->buf->create())
+ if (!buffer->buf->create()) {
qWarning("Failed to build vertex/index buffer of size %d", buffer->size);
+ delete buffer->buf;
+ buffer->buf = nullptr;
+ }
} else {
bool needsRebuild = false;
if (buffer->buf->size() < buffer->size) {
@@ -1070,16 +1054,23 @@ void Renderer::unmap(Buffer *buffer, bool isIndexBuf)
buffer->nonDynamicChangeCount = 0;
needsRebuild = true;
}
- if (needsRebuild)
- buffer->buf->create();
+ if (needsRebuild) {
+ if (!buffer->buf->create()) {
+ qWarning("Failed to (re)build vertex/index buffer of size %d", buffer->size);
+ delete buffer->buf;
+ buffer->buf = nullptr;
+ }
+ }
}
- if (buffer->buf->type() != QRhiBuffer::Dynamic) {
- m_resourceUpdates->uploadStaticBuffer(buffer->buf,
- 0, buffer->size, buffer->data);
- buffer->nonDynamicChangeCount += 1;
- } else {
- m_resourceUpdates->updateDynamicBuffer(buffer->buf, 0, buffer->size,
- buffer->data);
+ if (buffer->buf) {
+ if (buffer->buf->type() != QRhiBuffer::Dynamic) {
+ m_resourceUpdates->uploadStaticBuffer(buffer->buf,
+ 0, buffer->size, buffer->data);
+ buffer->nonDynamicChangeCount += 1;
+ } else {
+ m_resourceUpdates->updateDynamicBuffer(buffer->buf, 0, buffer->size,
+ buffer->data);
+ }
}
if (m_visualizer->mode() == Visualizer::VisualizeNothing)
buffer->data = nullptr;
@@ -2629,10 +2620,29 @@ static inline bool needsBlendConstant(QRhiGraphicsPipeline::BlendFactor f)
bool Renderer::ensurePipelineState(Element *e, const ShaderManager::Shader *sms, bool depthPostPass)
{
- // In unmerged batches the srbs in the elements are all compatible
- // layout-wise. Note the key's == and qHash implementations: the rp desc and
- // srb are tested for (layout) compatibility, not pointer equality.
- const GraphicsPipelineStateKey k { m_gstate, sms, renderPassDescriptor(), e->srb };
+ // Note the key's == and qHash implementations: the renderpass descriptor
+ // and srb are tested for compatibility, not pointer equality.
+ //
+ // We do not store the srb pointer itself because the ownership stays with
+ // the Element and that can go away more often that we would like it
+ // to. (think scrolling a list view, constantly dropping and creating new
+ // nodes) Rather, use an opaque blob of a few uints and store and compare
+ // that. This works because once the pipeline is built, we will always call
+ // setShaderResources with an explicitly specified srb which is fine even if
+ // e->srb we used here to bake the pipeline is already gone by that point.
+ //
+ // A typical QSGMaterial's serialized srb layout is 8 uints. (uniform buffer
+ // + texture, 4 fields each) Regardless, using an implicitly shared
+ // container is essential here. (won't detach so no more allocs and copies
+ // are done, unless the Element decides to rebake the srb with a different
+ // layout - but then the detach is exactly what we need)
+ //
+ // Same story for the renderpass descriptor: the object can go away but
+ // that's fine because that has no effect on an already built pipeline, and
+ // for comparison we only rely on the serialized blob in order decide if the
+ // render target is compatible with the pipeline.
+
+ const GraphicsPipelineStateKey k = GraphicsPipelineStateKey::create(m_gstate, sms, renderPassDescriptor(), e->srb);
// Note: dynamic state (viewport rect, scissor rect, stencil ref, blend
// constant) is never a part of GraphicsState/QRhiGraphicsPipeline.
@@ -2826,8 +2836,8 @@ static void materialToRendererGraphicsState(GraphicsState *dst,
void Renderer::updateMaterialDynamicData(ShaderManager::Shader *sms,
QSGMaterialShader::RenderState &renderState,
QSGMaterial *material,
- ShaderManager::ShaderResourceBindingList *bindings,
const Batch *batch,
+ Element *e,
int ubufOffset,
int ubufRegionSize)
{
@@ -2835,6 +2845,8 @@ void Renderer::updateMaterialDynamicData(ShaderManager::Shader *sms,
QSGMaterialShader *shader = sms->programRhi.program;
QSGMaterialShaderPrivate *pd = QSGMaterialShaderPrivate::get(shader);
+ QVarLengthArray<QRhiShaderResourceBinding, 8> bindings;
+
if (pd->ubufBinding >= 0) {
m_current_uniform_data = &pd->masterUniformData;
const bool changed = shader->updateUniformData(renderState, material, m_currentMaterial);
@@ -2843,11 +2855,11 @@ void Renderer::updateMaterialDynamicData(ShaderManager::Shader *sms,
if (changed || !batch->ubufDataValid)
m_resourceUpdates->updateDynamicBuffer(batch->ubuf, ubufOffset, ubufRegionSize, pd->masterUniformData.constData());
- bindings->append(QRhiShaderResourceBinding::uniformBuffer(pd->ubufBinding,
- pd->ubufStages,
- batch->ubuf,
- ubufOffset,
- ubufRegionSize));
+ bindings.append(QRhiShaderResourceBinding::uniformBuffer(pd->ubufBinding,
+ pd->ubufStages,
+ batch->ubuf,
+ ubufOffset,
+ ubufRegionSize));
}
for (int binding = 0; binding < QSGMaterialShaderPrivate::MAX_SHADER_RESOURCE_BINDINGS; ++binding) {
@@ -2901,17 +2913,92 @@ void Renderer::updateMaterialDynamicData(ShaderManager::Shader *sms,
if (!texture)
texture = dummyTexture();
QRhiSampler *sampler = pd->samplerBindingTable[binding];
- bindings->append(QRhiShaderResourceBinding::sampledTexture(binding,
- stages,
- texture,
- sampler));
+ bindings.append(QRhiShaderResourceBinding::sampledTexture(binding,
+ stages,
+ texture,
+ sampler));
}
}
#ifndef QT_NO_DEBUG
- if (bindings->isEmpty())
+ if (bindings.isEmpty())
qWarning("No shader resources for material %p, this is odd.", material);
#endif
+
+ enum class SrbAction {
+ Unknown,
+ DoNothing,
+ UpdateResources,
+ Rebake
+ } srbAction = SrbAction::Unknown;
+
+ // First, if the Element has no srb created at all, then try to find an existing,
+ // currently unused srb that is layout-compatible with our binding list.
+ if (!e->srb) {
+ // reuse a QVector as our work area, thus possibly reusing the underlying allocation too
+ QVector<quint32> &layoutDesc(m_shaderManager->srbLayoutDescSerializeWorkspace);
+ layoutDesc.clear();
+ QRhiShaderResourceBinding::serializeLayoutDescription(bindings.cbegin(), bindings.cend(), std::back_inserter(layoutDesc));
+ e->srb = m_shaderManager->srbPool.take(layoutDesc);
+ if (e->srb) {
+ // Here we know layout compatibility is satisfied, but do not spend time on full
+ // comparison. The chance of getting an srb that refers to the same resources
+ // (buffer, textures) is low in practice. So reuse, but write new resources.
+ srbAction = SrbAction::UpdateResources;
+ }
+ }
+
+ // If the Element had an existing srb, investigate:
+ // - It may be used as-is (when nothing changed in the scene regarding this node compared to the previous frame).
+ // - Otherwise it may be able to go with a lightweight update (replace resources, binding list layout is the same).
+ // - If all else fails rebake the full thing, meaning we reuse the memory allocation but will recreate everything underneath.
+ if (srbAction == SrbAction::Unknown && e->srb) {
+ if (std::equal(e->srb->cbeginBindings(), e->srb->cendBindings(), bindings.cbegin(), bindings.cend())) {
+ srbAction = SrbAction::DoNothing;
+ } else if (std::equal(e->srb->cbeginBindings(), e->srb->cendBindings(), bindings.cbegin(), bindings.cend(),
+ [](const auto &a, const auto &b) { return a.isLayoutCompatible(b); }))
+ {
+ srbAction = SrbAction::UpdateResources;
+ } else {
+ srbAction = SrbAction::Rebake;
+ }
+ }
+
+ // If the Element had no srb associated at all and could not find a layout-compatible
+ // one from the pool, then create a whole new object.
+ if (!e->srb) {
+ e->srb = m_rhi->newShaderResourceBindings();
+ srbAction = SrbAction::Rebake;
+ }
+
+ Q_ASSERT(srbAction != SrbAction::Unknown && e->srb);
+
+ switch (srbAction) {
+ case SrbAction::DoNothing:
+ break;
+ case SrbAction::UpdateResources:
+ {
+ e->srb->setBindings(bindings.cbegin(), bindings.cend());
+ QRhiShaderResourceBindings::UpdateFlags flags;
+ // Due to the way the binding list is built up above, if we have a uniform buffer
+ // at binding point 0 (or none at all) then the sampledTexture bindings are added
+ // with increasing binding points afterwards, so the list is already sorted based
+ // on the binding points, thus we can save some time by telling the QRhi backend
+ // not to sort again.
+ if (pd->ubufBinding <= 0 || bindings.count() <= 1)
+ flags |= QRhiShaderResourceBindings::BindingsAreSorted;
+
+ e->srb->updateResources(flags);
+ }
+ break;
+ case SrbAction::Rebake:
+ e->srb->setBindings(bindings.cbegin(), bindings.cend());
+ if (!e->srb->create())
+ qWarning("Failed to build srb");
+ break;
+ default:
+ Q_ASSERT_X(false, "updateMaterialDynamicData", "No srb action set, this cannot happen");
+ }
}
void Renderer::updateMaterialStaticData(ShaderManager::Shader *sms,
@@ -3029,8 +3116,7 @@ bool Renderer::prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *rende
bool pendingGStatePop = false;
updateMaterialStaticData(sms, renderState, material, batch, &pendingGStatePop);
- ShaderManager::ShaderResourceBindingList bindings;
- updateMaterialDynamicData(sms, renderState, material, &bindings, batch, 0, ubufSize);
+ updateMaterialDynamicData(sms, renderState, material, batch, e, 0, ubufSize);
#ifndef QT_NO_DEBUG
if (qsg_test_and_clear_material_failure()) {
@@ -3045,8 +3131,6 @@ bool Renderer::prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *rende
}
#endif
- e->srb = m_shaderManager->srb(bindings);
-
m_gstate.drawMode = QSGGeometry::DrawingMode(g->drawingMode());
m_gstate.lineWidth = g->lineWidth();
@@ -3103,6 +3187,9 @@ void Renderer::checkLineWidth(QSGGeometry *g)
void Renderer::renderMergedBatch(PreparedRenderBatch *renderBatch, bool depthPostPass)
{
const Batch *batch = renderBatch->batch;
+ if (!batch->vbo.buf || !batch->ibo.buf)
+ return;
+
Element *e = batch->first;
QSGGeometryNode *gn = e->node;
QSGGeometry *g = gn->geometry();
@@ -3232,9 +3319,7 @@ bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *ren
}
QSGMaterialShader::RenderState renderState = state(QSGMaterialShader::RenderState::DirtyStates(int(dirty)));
- ShaderManager::ShaderResourceBindingList bindings;
- updateMaterialDynamicData(sms, renderState,
- material, &bindings, batch, ubufOffset, ubufSize);
+ updateMaterialDynamicData(sms, renderState, material, batch, e, ubufOffset, ubufSize);
#ifndef QT_NO_DEBUG
if (qsg_test_and_clear_material_failure()) {
@@ -3246,8 +3331,6 @@ bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *ren
}
#endif
- e->srb = m_shaderManager->srb(bindings);
-
ubufOffset += aligned(ubufSize, m_ubufAlignment);
const QSGGeometry::DrawingMode prevDrawMode = m_gstate.drawMode;
@@ -3302,8 +3385,10 @@ bool Renderer::prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *ren
void Renderer::renderUnmergedBatch(PreparedRenderBatch *renderBatch, bool depthPostPass)
{
const Batch *batch = renderBatch->batch;
+ if (!batch->vbo.buf)
+ return;
+
Element *e = batch->first;
- QSGGeometryNode *gn = e->node;
if (batch->clipState.type & ClipState::StencilClip)
enqueueStencilDraw(batch);
@@ -3313,8 +3398,7 @@ void Renderer::renderUnmergedBatch(PreparedRenderBatch *renderBatch, bool depthP
QRhiCommandBuffer *cb = commandBuffer();
while (e) {
- gn = e->node;
- QSGGeometry *g = gn->geometry();
+ QSGGeometry *g = e->node->geometry();
checkLineWidth(g);
const int effectiveIndexSize = m_uint32IndexForRhi ? sizeof(quint32) : g->sizeOfIndex();
@@ -3322,11 +3406,13 @@ void Renderer::renderUnmergedBatch(PreparedRenderBatch *renderBatch, bool depthP
const QRhiCommandBuffer::VertexInput vbufBinding(batch->vbo.buf, vOffset);
if (g->indexCount()) {
- cb->setVertexInput(VERTEX_BUFFER_BINDING, 1, &vbufBinding,
- batch->ibo.buf, iOffset,
- effectiveIndexSize == sizeof(quint32) ? QRhiCommandBuffer::IndexUInt32
- : QRhiCommandBuffer::IndexUInt16);
- cb->drawIndexed(g->indexCount());
+ if (batch->ibo.buf) {
+ cb->setVertexInput(VERTEX_BUFFER_BINDING, 1, &vbufBinding,
+ batch->ibo.buf, iOffset,
+ effectiveIndexSize == sizeof(quint32) ? QRhiCommandBuffer::IndexUInt32
+ : QRhiCommandBuffer::IndexUInt16);
+ cb->drawIndexed(g->indexCount());
+ }
} else {
cb->setVertexInput(VERTEX_BUFFER_BINDING, 1, &vbufBinding);
cb->draw(g->vertexCount());
@@ -3372,6 +3458,26 @@ void Renderer::setGraphicsPipeline(QRhiCommandBuffer *cb, const Batch *batch, El
cb->setShaderResources(e->srb);
}
+void Renderer::releaseElement(Element *e, bool inDestructor)
+{
+ if (e->isRenderNode) {
+ delete static_cast<RenderNodeElement *>(e);
+ } else {
+ if (e->srb) {
+ if (!inDestructor) {
+ if (m_shaderManager->srbPool.count() < m_srbPoolThreshold)
+ m_shaderManager->srbPool.insert(e->srb->serializedLayoutDescription(), e->srb);
+ else
+ delete e->srb;
+ } else {
+ delete e->srb;
+ }
+ e->srb = nullptr;
+ }
+ m_elementAllocator.release(e);
+ }
+}
+
void Renderer::deleteRemovedElements()
{
if (!m_elementsToDelete.size())
@@ -3388,13 +3494,9 @@ void Renderer::deleteRemovedElements()
*e = nullptr;
}
- for (int i=0; i<m_elementsToDelete.size(); ++i) {
- Element *e = m_elementsToDelete.at(i);
- if (e->isRenderNode)
- delete static_cast<RenderNodeElement *>(e);
- else
- m_elementAllocator.release(e);
- }
+ for (int i=0; i<m_elementsToDelete.size(); ++i)
+ releaseElement(m_elementsToDelete.at(i));
+
m_elementsToDelete.reset();
}
@@ -3903,22 +4005,6 @@ bool Renderer::hasVisualizationModeWithContinuousUpdate() const
return m_visualizer->mode() == Visualizer::VisualizeOverdraw;
}
-void Renderer::invalidatePipelineCacheDependency(QRhiRenderPassDescriptor *rpDesc)
-{
- if (!rpDesc)
- return;
-
- for (auto it = m_shaderManager->pipelineCache.begin(); it != m_shaderManager->pipelineCache.end(); ) {
- if (it.key().compatibleRenderPassDescriptor == rpDesc) {
- QRhiGraphicsPipeline *ps = it.value();
- it = m_shaderManager->pipelineCache.erase(it);
- ps->deleteLater(); // QRhi takes care of it in endFrame()
- } else {
- ++it;
- }
- }
-}
-
bool operator==(const GraphicsState &a, const GraphicsState &b) noexcept
{
return a.depthTest == b.depthTest
@@ -3960,8 +4046,8 @@ bool operator==(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKe
{
return a.state == b.state
&& a.sms->programRhi.program == b.sms->programRhi.program
- && a.compatibleRenderPassDescriptor->isCompatible(b.compatibleRenderPassDescriptor)
- && a.layoutCompatibleSrb->isLayoutCompatible(b.layoutCompatibleSrb);
+ && a.renderTargetDescription == b.renderTargetDescription
+ && a.srbLayoutDescription == b.srbLayoutDescription;
}
bool operator!=(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) noexcept
@@ -3971,8 +4057,10 @@ bool operator!=(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKe
size_t qHash(const GraphicsPipelineStateKey &k, size_t seed) noexcept
{
- // no srb and rp included due to their special comparison semantics and lack of hash keys
- return qHash(k.state, seed) + qHash(k.sms->programRhi.program, seed);
+ return qHash(k.state, seed)
+ ^ qHash(k.sms->programRhi.program)
+ ^ k.extra.renderTargetDescriptionHash
+ ^ k.extra.srbLayoutDescriptionHash;
}
Visualizer::Visualizer(Renderer *renderer)
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
index a42c41304f..241f5748b6 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
@@ -652,8 +652,21 @@ struct GraphicsPipelineStateKey
{
GraphicsState state;
const ShaderManagerShader *sms;
- const QRhiRenderPassDescriptor *compatibleRenderPassDescriptor;
- const QRhiShaderResourceBindings *layoutCompatibleSrb;
+ QVector<quint32> renderTargetDescription;
+ QVector<quint32> srbLayoutDescription;
+ struct {
+ size_t renderTargetDescriptionHash;
+ size_t srbLayoutDescriptionHash;
+ } extra;
+ static GraphicsPipelineStateKey create(const GraphicsState &state,
+ const ShaderManagerShader *sms,
+ const QRhiRenderPassDescriptor *rpDesc,
+ const QRhiShaderResourceBindings *srb)
+ {
+ const QVector<quint32> rtDesc = rpDesc->serializedFormat();
+ const QVector<quint32> srbDesc = srb->serializedLayoutDescription();
+ return { state, sms, rtDesc, srbDesc, { qHash(rtDesc), qHash(srbDesc) } };
+ }
};
bool operator==(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) noexcept;
@@ -687,11 +700,11 @@ public:
void clearCachedRendererData();
- using ShaderResourceBindingList = QVarLengthArray<QRhiShaderResourceBinding, 8>;
- QRhiShaderResourceBindings *srb(const ShaderResourceBindingList &bindings);
-
QHash<GraphicsPipelineStateKey, QRhiGraphicsPipeline *> pipelineCache;
+ QMultiHash<QVector<quint32>, QRhiShaderResourceBindings *> srbPool;
+ QVector<quint32> srbLayoutDescSerializeWorkspace;
+
public Q_SLOTS:
void invalidated();
@@ -705,8 +718,6 @@ private:
QHash<ShaderKey, Shader *> stockShaders;
QSGDefaultRenderContext *context;
-
- QHash<ShaderResourceBindingList, QRhiShaderResourceBindings *> srbCache;
};
struct RenderPassState
@@ -822,8 +833,7 @@ private:
bool ensurePipelineState(Element *e, const ShaderManager::Shader *sms, bool depthPostPass = false);
QRhiTexture *dummyTexture();
void updateMaterialDynamicData(ShaderManager::Shader *sms, QSGMaterialShader::RenderState &renderState,
- QSGMaterial *material, ShaderManager::ShaderResourceBindingList *bindings,
- const Batch *batch, int ubufOffset, int ubufRegionSize);
+ QSGMaterial *material, const Batch *batch, Element *e, int ubufOffset, int ubufRegionSize);
void updateMaterialStaticData(ShaderManager::Shader *sms, QSGMaterialShader::RenderState &renderState,
QSGMaterial *material, Batch *batch, bool *gstateChanged);
void checkLineWidth(QSGGeometry *g);
@@ -858,12 +868,11 @@ private:
inline Batch *newBatch();
void invalidateAndRecycleBatch(Batch *b);
+ void releaseElement(Element *e, bool inDestructor = false);
void setVisualizationMode(const QByteArray &mode) override;
bool hasVisualizationModeWithContinuousUpdate() const override;
- void invalidatePipelineCacheDependency(QRhiRenderPassDescriptor *rpDesc) override;
-
QSGDefaultRenderContext *m_context;
QSGRendererInterface::RenderMode m_renderMode;
QSet<Node *> m_taggedRoots;
@@ -893,6 +902,7 @@ private:
int m_batchNodeThreshold;
int m_batchVertexThreshold;
+ int m_srbPoolThreshold;
Visualizer *m_visualizer;
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.cpp b/src/quick/scenegraph/coreapi/qsggeometry.cpp
index c8ecf49767..cf369f55a6 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.cpp
+++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp
@@ -406,8 +406,10 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D()
Geometry objects are constructed by default with DrawTriangleStrip as
the drawing mode.
- The attribute structure is assumed to be POD and the geometry object
- assumes this will not go away. There is no memory management involved.
+ \note \a attributes and the \l Attribute objects referenced by it must
+ stay valid for the entire lifetime of the QSGGeometry.
+ QSGGeometry stores a reference to \a attributes and does not delete
+ the \l Attribute objects.
*/
QSGGeometry::QSGGeometry(const QSGGeometry::AttributeSet &attributes,
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.h b/src/quick/scenegraph/coreapi/qsggeometry.h
index 060d896b3c..82b930edf2 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.h
+++ b/src/quick/scenegraph/coreapi/qsggeometry.h
@@ -201,6 +201,7 @@ public:
void setLineWidth(float w);
private:
+ Q_DISABLE_COPY_MOVE(QSGGeometry)
friend class QSGGeometryData;
int m_drawing_mode;
diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp
index e22ffa10d2..46637b326e 100644
--- a/src/quick/scenegraph/coreapi/qsgnode.cpp
+++ b/src/quick/scenegraph/coreapi/qsgnode.cpp
@@ -78,8 +78,8 @@ static void qt_print_node_count()
insertChildNodeAfter(). The order of nodes is important as geometry nodes
are rendered according to their ordering in the scene graph.
- The scene graph nodes contains a mechanism to describe which
- parts of the scene has changed. This includes the combined matrices,
+ The scene graph nodes contain a mechanism that describes which
+ parts of the scene have changed. This includes the combined matrices,
accumulated opacity, changes to the node hierarchy, and so on. This
information can be used for optimizations inside the scene graph renderer.
For the renderer to properly render the nodes, it is important that users
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
index 4b3cd9f2c1..277e5b8482 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
@@ -55,7 +55,7 @@ int qt_sg_envInt(const char *name, int defaultValue)
if (Q_LIKELY(!qEnvironmentVariableIsSet(name)))
return defaultValue;
bool ok = false;
- int value = qgetenv(name).toInt(&ok);
+ int value = qEnvironmentVariableIntValue(name, &ok);
return ok ? value : defaultValue;
}
@@ -98,7 +98,6 @@ QSGRenderer::QSGRenderer(QSGRenderContext *context)
, m_rt(nullptr)
, m_cb(nullptr)
, m_rp_desc(nullptr)
- , m_external_rp_desc(nullptr)
, m_node_updater(nullptr)
, m_changed_emitted(false)
, m_is_rendering(false)
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
index 31f00dc738..9f1ab6a99b 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
@@ -97,7 +97,6 @@ public:
virtual void setVisualizationMode(const QByteArray &) { }
virtual bool hasVisualizationModeWithContinuousUpdate() const { return false; }
virtual void releaseCachedResources() { }
- virtual void invalidatePipelineCacheDependency(QRhiRenderPassDescriptor *) { }
void clearChangedFlag() { m_changed_emitted = false; }
@@ -116,15 +115,8 @@ public:
QRhiRenderPassDescriptor *renderPassDescriptor() const { return m_rp_desc; }
void setExternalRenderPassDescriptor(QRhiRenderPassDescriptor *rpDesc) {
- if (m_external_rp_desc) {
- // Changes will be rare in practice - one has to construct a
- // dynamic Quick 3D scene with reparenting involved for that. Play
- // nice nonetheless and invalidate as soon as possible.
- if (m_external_rp_desc != rpDesc)
- invalidatePipelineCacheDependency(m_external_rp_desc);
- }
- m_rp_desc = rpDesc;
- m_external_rp_desc = rpDesc;
+ // no differentiation needed anymore
+ setRenderPassDescriptor(rpDesc);
}
void setRenderPassRecordingCallbacks(QSGRenderContext::RenderPassCallback start,
@@ -162,7 +154,6 @@ protected:
QRhiRenderTarget *m_rt;
QRhiCommandBuffer *m_cb;
QRhiRenderPassDescriptor *m_rp_desc;
- QRhiRenderPassDescriptor *m_external_rp_desc;
struct {
QSGRenderContext::RenderPassCallback start = nullptr;
QSGRenderContext::RenderPassCallback end = nullptr;
diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp
index ec8e3dda57..403206abc5 100644
--- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp
@@ -239,13 +239,14 @@ void QSGRenderNode::prepare()
Assume nothing about the pipelines and dynamic states bound on the command
list/buffer when this function is called.
- With some graphics APIs it can be necessary to also connect to the
- QQuickWindow::beforeRendering() signal, because that is emitted before
- recording the beginning of a renderpass on the command buffer
- (vkCmdBeginRenderPass with Vulkan, or starting to encode via
- MTLRenderCommandEncoder in case of Metal). Recording copy operations cannot
- be done inside render() with such APIs. Rather, do it in the slot connected
- (with DirectConnection) to the beforeRendering signal.
+ With some graphics APIs it can be necessary to reimplement prepare() in
+ addition, or alternatively connect to the QQuickWindow::beforeRendering()
+ signal. These are called/emitted before recording the beginning of a
+ renderpass on the command buffer (vkCmdBeginRenderPass with Vulkan, or
+ starting to encode via MTLRenderCommandEncoder in case of Metal. Recording
+ copy operations cannot be done inside render() with such APIs. Rather, do
+ such operations either in prepare() or the slot connected to
+ beforeRendering (with DirectConnection).
\sa QSGRendererInterface, QQuickWindow::rendererInterface()
*/
diff --git a/src/quick/scenegraph/coreapi/qsgtexture.cpp b/src/quick/scenegraph/coreapi/qsgtexture.cpp
index d70a9664ba..b79999c3ea 100644
--- a/src/quick/scenegraph/coreapi/qsgtexture.cpp
+++ b/src/quick/scenegraph/coreapi/qsgtexture.cpp
@@ -43,6 +43,7 @@
#include <private/qsgmaterialshader_p.h>
#include <private/qquickitem_p.h> // qquickwindow_p.h cannot be included on its own due to template nonsense
#include <private/qquickwindow_p.h>
+#include <QtCore/private/qnativeinterface_p.h>
#include <QtGui/private/qrhi_p.h>
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && defined(__GLIBC__)
@@ -715,6 +716,7 @@ namespace QNativeInterface {
\inmodule QtQuick
\ingroup native-interfaces
\ingroup native-interfaces-qsgtexture
+ \inheaderfile QSGTexture
\brief Provides access to and enables adopting OpenGL texture objects.
\since 6.0
*/
@@ -815,6 +817,7 @@ namespace QNativeInterface {
\inmodule QtQuick
\ingroup native-interfaces
\ingroup native-interfaces-qsgtexture
+ \inheaderfile QSGTexture
\brief Provides access to and enables adopting Direct3D 11 texture objects.
\since 6.0
*/
@@ -875,6 +878,7 @@ namespace QNativeInterface {
\inmodule QtQuick
\ingroup native-interfaces
\ingroup native-interfaces-qsgtexture
+ \inheaderfile QSGTexture
\brief Provides access to and enables adopting Metal texture objects.
\since 6.0
*/
@@ -922,6 +926,7 @@ namespace QNativeInterface {
\inmodule QtQuick
\ingroup native-interfaces
\ingroup native-interfaces-qsgtexture
+ \inheaderfile QSGTexture
\brief Provides access to and enables adopting Vulkan image objects.
\since 6.0
*/
diff --git a/src/quick/scenegraph/coreapi/qsgtexture_mac.mm b/src/quick/scenegraph/coreapi/qsgtexture_mac.mm
index 9d009eb5cb..7ead16f14f 100644
--- a/src/quick/scenegraph/coreapi/qsgtexture_mac.mm
+++ b/src/quick/scenegraph/coreapi/qsgtexture_mac.mm
@@ -41,6 +41,7 @@
#include "qsgtexture_platform.h"
#include <private/qquickitem_p.h>
#include <private/qquickwindow_p.h>
+#include <QtCore/private/qnativeinterface_p.h>
#include <QtGui/private/qrhi_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h
index ee0dc95945..c3d286ee9d 100644
--- a/src/quick/scenegraph/qsgadaptationlayer_p.h
+++ b/src/quick/scenegraph/qsgadaptationlayer_p.h
@@ -353,6 +353,7 @@ public:
};
ShaderSyncData vertex;
ShaderSyncData fragment;
+ void *materialTypeCacheKey;
};
// Each ShaderEffect item has one node (render thread) and one manager (gui thread).
@@ -463,9 +464,9 @@ public:
{
return pixelSize / baseFontSize();
}
- int distanceFieldRadius() const
+ qreal distanceFieldRadius() const
{
- return QT_DISTANCEFIELD_RADIUS(m_doubleGlyphResolution) / QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution);
+ return QT_DISTANCEFIELD_RADIUS(m_doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution));
}
int glyphCount() const { return m_glyphCount; }
bool doubleGlyphResolution() const { return m_doubleGlyphResolution; }
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
index 578b5ab2f2..df0592cad1 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
@@ -111,6 +111,18 @@ QSGTextMaskRhiShader::QSGTextMaskRhiShader(QFontEngine::GlyphFormat glyphFormat)
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/textmask.frag.qsb"));
}
+enum UbufOffset {
+ ModelViewMatrixOffset = 0,
+ ProjectionMatrixOffset = ModelViewMatrixOffset + 64,
+ ColorOffset = ProjectionMatrixOffset + 64,
+ TextureScaleOffset = ColorOffset + 16,
+ DprOffset = TextureScaleOffset + 8,
+
+ // + 1 float padding (vec4 must be aligned to 16)
+ StyleColorOffset = DprOffset + 4 + 4,
+ ShiftOffset = StyleColorOffset + 16
+};
+
bool QSGTextMaskRhiShader::updateUniformData(RenderState &state,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
{
@@ -126,11 +138,14 @@ bool QSGTextMaskRhiShader::updateUniformData(RenderState &state,
bool changed = false;
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= 92);
+ Q_ASSERT(buf->size() >= DprOffset + 4);
if (state.isMatrixDirty()) {
- const QMatrix4x4 m = state.combinedMatrix();
- memcpy(buf->data(), m.constData(), 64);
+ const QMatrix4x4 mv = state.modelViewMatrix();
+ memcpy(buf->data() + ModelViewMatrixOffset, mv.constData(), 64);
+ const QMatrix4x4 p = state.projectionMatrix();
+ memcpy(buf->data() + ProjectionMatrixOffset, p.constData(), 64);
+
changed = true;
}
@@ -139,13 +154,13 @@ bool QSGTextMaskRhiShader::updateUniformData(RenderState &state,
if (updated || !oldMat || oldRtex != newRtex) {
const QVector2D textureScale = QVector2D(1.0f / mat->rhiGlyphCache()->width(),
1.0f / mat->rhiGlyphCache()->height());
- memcpy(buf->data() + 64 + 16, &textureScale, 8);
+ memcpy(buf->data() + TextureScaleOffset, &textureScale, 8);
changed = true;
}
if (!oldMat) {
float dpr = state.devicePixelRatio();
- memcpy(buf->data() + 64 + 16 + 8, &dpr, 4);
+ memcpy(buf->data() + DprOffset, &dpr, 4);
}
// move texture uploads/copies onto the renderer's soon-to-be-committed list
@@ -193,11 +208,11 @@ bool QSG8BitTextMaskRhiShader::updateUniformData(RenderState &state,
QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial);
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= 80);
+ Q_ASSERT(buf->size() >= ColorOffset + 16);
if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) {
const QVector4D color = qsg_premultiply(mat->color(), state.opacity());
- memcpy(buf->data() + 64, &color, 16);
+ memcpy(buf->data() + ColorOffset, &color, 16);
changed = true;
}
@@ -236,12 +251,12 @@ bool QSG24BitTextMaskRhiShader::updateUniformData(RenderState &state,
QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial);
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= 92);
+ Q_ASSERT(buf->size() >= ColorOffset + 16);
if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) {
// shader takes vec4 but uses alpha only; coloring happens via the blend constant
const QVector4D color = qsg_premultiply(mat->color(), state.opacity());
- memcpy(buf->data() + 64, &color, 16);
+ memcpy(buf->data() + ColorOffset, &color, 16);
changed = true;
}
@@ -259,7 +274,8 @@ bool QSG24BitTextMaskRhiShader::updateGraphicsPipelineState(RenderState &state,
ps->srcColor = GraphicsPipelineState::ConstantColor;
ps->dstColor = GraphicsPipelineState::OneMinusSrcColor;
- QVector4D color = qsg_premultiply(mat->color(), state.opacity());
+ QVector4D color = mat->color();
+
// if (useSRGB())
// color = qt_sRGB_to_linear_RGB(color);
@@ -291,12 +307,12 @@ bool QSG32BitColorTextRhiShader::updateUniformData(RenderState &state,
QSGTextMaskMaterial *oldMat = static_cast<QSGTextMaskMaterial *>(oldMaterial);
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= 92);
+ Q_ASSERT(buf->size() >= ColorOffset + 16);
if (oldMat == nullptr || mat->color() != oldMat->color() || state.isOpacityDirty()) {
// shader takes vec4 but uses alpha only
const QVector4D color(0, 0, 0, mat->color().w() * state.opacity());
- memcpy(buf->data() + 64, &color, 16);
+ memcpy(buf->data() + ColorOffset, &color, 16);
changed = true;
}
@@ -332,20 +348,17 @@ bool QSGStyledTextRhiShader::updateUniformData(RenderState &state,
QSGStyledTextMaterial *oldMat = static_cast<QSGStyledTextMaterial *>(oldMaterial);
QByteArray *buf = state.uniformData();
- Q_ASSERT(buf->size() >= 120);
-
- // matrix..dpr + 1 float padding (vec4 must be aligned to 16)
- const int startOffset = 64 + 16 + 8 + 4 + 4;
+ Q_ASSERT(buf->size() >= ShiftOffset + 8);
if (oldMat == nullptr || mat->styleColor() != oldMat->styleColor() || state.isOpacityDirty()) {
const QVector4D styleColor = qsg_premultiply(mat->styleColor(), state.opacity());
- memcpy(buf->data() + startOffset, &styleColor, 16);
+ memcpy(buf->data() + StyleColorOffset, &styleColor, 16);
changed = true;
}
if (oldMat == nullptr || oldMat->styleShift() != mat->styleShift()) {
const QVector2D v = mat->styleShift();
- memcpy(buf->data() + startOffset + 16, &v, 8);
+ memcpy(buf->data() + ShiftOffset, &v, 8);
changed = true;
}
@@ -444,7 +457,7 @@ void QSGTextMaskMaterial::updateCache(QFontEngine::GlyphFormat glyphFormat)
QColor color = glyphFormat == QFontEngine::Format_ARGB ? QColor::fromRgbF(m_color.x(), m_color.y(), m_color.z(), m_color.w()) : QColor();
m_glyphCache = fontEngine->glyphCache(cacheKey, glyphFormat, glyphCacheTransform, color);
if (!m_glyphCache || int(m_glyphCache->glyphFormat()) != glyphFormat) {
- m_glyphCache = new QSGRhiTextureGlyphCache(m_rhi, glyphFormat, glyphCacheTransform, color);
+ m_glyphCache = new QSGRhiTextureGlyphCache(m_rc, glyphFormat, glyphCacheTransform, color);
fontEngine->setGlyphCache(cacheKey, m_glyphCache.data());
m_rc->registerFontengineForCleanup(fontEngine);
}
@@ -470,8 +483,12 @@ void QSGTextMaskMaterial::populate(const QPointF &p,
QTextureGlyphCache *cache = glyphCache();
QRawFontPrivate *fontD = QRawFontPrivate::get(m_font);
- cache->populate(fontD->fontEngine, glyphIndexes.size(), glyphIndexes.constData(),
- fixedPointPositions.data());
+ cache->populate(fontD->fontEngine,
+ glyphIndexes.size(),
+ glyphIndexes.constData(),
+ fixedPointPositions.data(),
+ QPainter::RenderHints(),
+ true);
cache->fillInPendingGlyphs();
int margin = fontD->fontEngine->glyphMargin(cache->glyphFormat());
@@ -491,9 +508,11 @@ void QSGTextMaskMaterial::populate(const QPointF &p,
bool supportsSubPixelPositions = fontD->fontEngine->supportsHorizontalSubPixelPositions();
for (int i=0; i<glyphIndexes.size(); ++i) {
QPointF glyphPosition = glyphPositions.at(i) + position;
+ QFixedPoint fixedPointPosition = fixedPointPositions.at(i);
+
QFixed subPixelPosition;
if (supportsSubPixelPositions)
- subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(glyphPosition.x()));
+ subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(fixedPointPosition.x.toReal() * glyphCacheScaleX));
QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphIndexes.at(i),
QFixedPoint(subPixelPosition, 0));
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
index 1b2dbab84f..8579cb5e2a 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp
+++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
@@ -64,6 +64,7 @@ QSGDefaultRenderContext::QSGDefaultRenderContext(QSGContext *context)
, m_currentFrameRenderPass(nullptr)
, m_separateIndexBuffer(false)
, m_useDepthBufferFor2D(true)
+ , m_glyphCacheResourceUpdates(nullptr)
{
}
@@ -89,6 +90,8 @@ void QSGDefaultRenderContext::initialize(const QSGRenderContext::InitParams *par
// unlike OpenGL (and like WebGL), QRhi does not guarantee buffer usage types can be mixed
m_separateIndexBuffer = true;
+ m_glyphCacheResourceUpdates = nullptr;
+
m_sg->renderContextInitialized(this);
emit initialized();
@@ -140,10 +143,11 @@ void QSGDefaultRenderContext::invalidate()
}
m_fontEnginesToClean.clear();
-
qDeleteAll(m_glyphCaches);
m_glyphCaches.clear();
+ releaseGlyphCacheResourceUpdates();
+
m_rhi = nullptr;
if (m_sg)
@@ -296,13 +300,34 @@ QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(con
QString key = fontKey(font, renderTypeQuality);
QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(key, 0);
if (!cache) {
- cache = new QSGRhiDistanceFieldGlyphCache(m_rhi, font, renderTypeQuality);
+ cache = new QSGRhiDistanceFieldGlyphCache(this, font, renderTypeQuality);
m_glyphCaches.insert(key, cache);
}
return cache;
}
+QRhiResourceUpdateBatch *QSGDefaultRenderContext::maybeGlyphCacheResourceUpdates()
+{
+ return m_glyphCacheResourceUpdates;
+}
+
+QRhiResourceUpdateBatch *QSGDefaultRenderContext::glyphCacheResourceUpdates()
+{
+ if (!m_glyphCacheResourceUpdates)
+ m_glyphCacheResourceUpdates = m_rhi->nextResourceUpdateBatch();
+
+ return m_glyphCacheResourceUpdates;
+}
+
+void QSGDefaultRenderContext::releaseGlyphCacheResourceUpdates()
+{
+ if (m_glyphCacheResourceUpdates) {
+ m_glyphCacheResourceUpdates->release();
+ m_glyphCacheResourceUpdates = nullptr;
+ }
+}
+
QT_END_NAMESPACE
#include "moc_qsgdefaultrendercontext_p.cpp"
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
index 002513c0e4..e96bf045b5 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext_p.h
+++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
@@ -59,6 +59,7 @@ QT_BEGIN_NAMESPACE
class QRhi;
class QRhiCommandBuffer;
class QRhiRenderPassDescriptor;
+class QRhiResourceUpdateBatch;
class QSGMaterialShader;
class QSurface;
@@ -143,6 +144,10 @@ public:
return m_currentDevicePixelRatio;
}
+ QRhiResourceUpdateBatch *maybeGlyphCacheResourceUpdates();
+ QRhiResourceUpdateBatch *glyphCacheResourceUpdates();
+ void releaseGlyphCacheResourceUpdates();
+
protected:
static QString fontKey(const QRawFont &font, int renderTypeQuality);
@@ -156,6 +161,7 @@ protected:
qreal m_currentDevicePixelRatio;
bool m_separateIndexBuffer;
bool m_useDepthBufferFor2D;
+ QRhiResourceUpdateBatch *m_glyphCacheResourceUpdates;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
index 8ac112b106..4c6589d1f9 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp
@@ -43,6 +43,10 @@
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcSgText, "qt.scenegraph.text")
+
+qint64 QSGDistanceFieldGlyphNode::m_totalAllocation = 0;
+
QSGDistanceFieldGlyphNode::QSGDistanceFieldGlyphNode(QSGRenderContext *context)
: m_glyphNodeType(RootGlyphNode)
, m_context(context)
@@ -135,6 +139,9 @@ void QSGDistanceFieldGlyphNode::setGlyphs(const QPointF &position, const QGlyphR
const QVector<quint32> glyphIndexes = m_glyphs.glyphIndexes();
for (int i = 0; i < glyphIndexes.count(); ++i)
m_allGlyphIndexesLookup.insert(glyphIndexes.at(i));
+ qCDebug(lcSgText, "inserting %" PRIdQSIZETYPE " glyphs, %" PRIdQSIZETYPE " unique",
+ glyphIndexes.count(),
+ m_allGlyphIndexesLookup.count());
}
void QSGDistanceFieldGlyphNode::setStyle(QQuickText::TextStyle style)
@@ -198,8 +205,7 @@ void QSGDistanceFieldGlyphNode::updateGeometry()
QSGGeometry *g = geometry();
Q_ASSERT(g->indexType() == QSGGeometry::UnsignedShortType);
-
- QHash<const QSGDistanceFieldGlyphCache::Texture *, GlyphInfo> glyphsInOtherTextures;
+ m_glyphsInOtherTextures.clear();
const QVector<quint32> indexes = m_glyphs.glyphIndexes();
const QVector<QPointF> positions = m_glyphs.positions();
@@ -208,9 +214,12 @@ void QSGDistanceFieldGlyphNode::updateGeometry()
// The template parameters here are assuming that most strings are short, 64
// characters or less.
QVarLengthArray<QSGGeometry::TexturedPoint2D, 256> vp;
- vp.reserve(indexes.size() * 4);
QVarLengthArray<ushort, 384> ip;
- ip.reserve(indexes.size() * 6);
+ const qsizetype maxIndexCount = (std::numeric_limits<quint16>::max() - 1) / 4; // 16383 (see below: 0xFFFF is not allowed)
+ const qsizetype maxVertexCount = maxIndexCount * 4; // 65532
+ const auto likelyGlyphCount = qMin(indexes.size(), maxIndexCount);
+ vp.reserve(likelyGlyphCount * 4);
+ ip.reserve(likelyGlyphCount * 6);
qreal maxTexMargin = m_glyph_cache->distanceFieldRadius();
qreal fontScale = m_glyph_cache->fontScale(fontPixelSize);
@@ -236,15 +245,20 @@ void QSGDistanceFieldGlyphNode::updateGeometry()
// As we use UNSIGNED_SHORT indexing in the geometry, we overload the
// "glyphsInOtherTextures" concept as overflow for if there are more
- // than 65535 vertices to render which would otherwise exceed the
+ // than 65532 vertices to render, which would otherwise exceed the
// maximum index size. (leave 0xFFFF unused in order not to clash with
- // primitive restart) This will cause sub-nodes to be recursively
- // created to handle any number of glyphs.
- if (m_texture != texture || vp.size() >= 65535) {
- if (texture->texture) {
- GlyphInfo &glyphInfo = glyphsInOtherTextures[texture];
+ // primitive restart) This will cause sub-nodes to be
+ // created to handle any number of glyphs. But only the RootGlyphNode
+ // needs to do this classification; from the perspective of a SubGlyphNode,
+ // it's already done, and m_glyphs contains only pointers to ranges of
+ // indices and positions that the RootGlyphNode is storing.
+ if (m_texture != texture || vp.size() >= maxVertexCount) {
+ if (m_glyphNodeType == RootGlyphNode && texture->texture) {
+ GlyphInfo &glyphInfo = m_glyphsInOtherTextures[texture];
glyphInfo.indexes.append(glyphIndex);
glyphInfo.positions.append(position);
+ } else if (vp.size() >= maxVertexCount && m_glyphNodeType == SubGlyphNode) {
+ break; // out of this loop over indices, because we won't add any more vertices
}
continue;
}
@@ -303,26 +317,38 @@ void QSGDistanceFieldGlyphNode::updateGeometry()
ip.append(o + 0);
}
- QHash<const QSGDistanceFieldGlyphCache::Texture *, GlyphInfo>::const_iterator ite = glyphsInOtherTextures.constBegin();
- while (ite != glyphsInOtherTextures.constEnd()) {
- QGlyphRun subNodeGlyphRun(m_glyphs);
- subNodeGlyphRun.setGlyphIndexes(ite->indexes);
- subNodeGlyphRun.setPositions(ite->positions);
-
- QSGDistanceFieldGlyphNode *subNode = new QSGDistanceFieldGlyphNode(m_context);
- subNode->setGlyphNodeType(SubGlyphNode);
- subNode->setColor(m_color);
- subNode->setStyle(m_style);
- subNode->setStyleColor(m_styleColor);
- subNode->setPreferredAntialiasingMode(m_antialiasingMode);
- subNode->setGlyphs(m_originalPosition, subNodeGlyphRun);
- subNode->update();
- subNode->updateGeometry(); // we have to explicitly call this now as preprocess won't be called before it's rendered
- appendChildNode(subNode);
-
- ++ite;
+ if (m_glyphNodeType == SubGlyphNode) {
+ Q_ASSERT(m_glyphsInOtherTextures.isEmpty());
+ } else {
+ if (!m_glyphsInOtherTextures.isEmpty())
+ qCDebug(lcSgText, "%" PRIdQSIZETYPE " 'other' textures", m_glyphsInOtherTextures.count());
+ QHash<const QSGDistanceFieldGlyphCache::Texture *, GlyphInfo>::const_iterator ite = m_glyphsInOtherTextures.constBegin();
+ while (ite != m_glyphsInOtherTextures.constEnd()) {
+ QGlyphRun subNodeGlyphRun(m_glyphs);
+ for (int i = 0; i < ite->indexes.count(); i += maxIndexCount) {
+ int len = qMin(maxIndexCount, ite->indexes.count() - i);
+ subNodeGlyphRun.setRawData(ite->indexes.constData() + i, ite->positions.constData() + i, len);
+ qCDebug(lcSgText) << "subNodeGlyphRun has" << len << "positions:"
+ << *(ite->positions.constData() + i) << "->" << *(ite->positions.constData() + i + len - 1);
+
+ QSGDistanceFieldGlyphNode *subNode = new QSGDistanceFieldGlyphNode(m_context);
+ subNode->setGlyphNodeType(SubGlyphNode);
+ subNode->setColor(m_color);
+ subNode->setStyle(m_style);
+ subNode->setStyleColor(m_styleColor);
+ subNode->setPreferredAntialiasingMode(m_antialiasingMode);
+ subNode->setGlyphs(m_originalPosition, subNodeGlyphRun);
+ subNode->update();
+ subNode->updateGeometry(); // we have to explicitly call this now as preprocess won't be called before it's rendered
+ appendChildNode(subNode);
+ }
+ ++ite;
+ }
}
+ m_totalAllocation += vp.size() * sizeof(QSGGeometry::TexturedPoint2D) + ip.size() * sizeof(quint16);
+ qCDebug(lcSgText) << "allocating for" << vp.size() << "vtx (reserved" << likelyGlyphCount * 4 << "):" << vp.size() * sizeof(QSGGeometry::TexturedPoint2D)
+ << "bytes;" << ip.size() << "idx:" << ip.size() * sizeof(quint16) << "bytes; total bytes so far" << m_totalAllocation;
g->allocate(vp.size(), ip.size());
memcpy(g->vertexDataAsTexturedPoint2D(), vp.constData(), vp.size() * sizeof(QSGGeometry::TexturedPoint2D));
memcpy(g->indexDataAsUShort(), ip.constData(), ip.size() * sizeof(quint16));
diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
index 2b2975ccb3..e7655f502d 100644
--- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
+++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h
@@ -115,9 +115,13 @@ private:
QVector<QPointF> positions;
};
QSet<quint32> m_allGlyphIndexesLookup;
+ // m_glyphs holds pointers to the GlyphInfo.indexes and positions arrays, so we need to hold on to them
+ QHash<const QSGDistanceFieldGlyphCache::Texture *, GlyphInfo> m_glyphsInOtherTextures;
uint m_dirtyGeometry: 1;
uint m_dirtyMaterial: 1;
+
+ static qint64 m_totalAllocation; // all SG glyph vertices and indices; only for qCDebug metrics
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index 077814b1f4..443afb00ac 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -357,10 +357,6 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window)
rhi->makeThreadLocalNativeContextCurrent();
}
-#if QT_CONFIG(quick_shadereffect)
- QSGRhiShaderEffectNode::cleanupMaterialTypeCache();
-#endif
-
if (d->swapchain) {
if (window->handle()) {
// We get here when exiting via QCoreApplication::quit() instead of
@@ -373,6 +369,11 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window)
}
d->cleanupNodesOnShutdown();
+
+#if QT_CONFIG(quick_shadereffect)
+ QSGRhiShaderEffectNode::cleanupMaterialTypeCache(window);
+#endif
+
if (m_windows.size() == 0) {
rc->invalidate();
d->rhi = nullptr;
@@ -409,17 +410,6 @@ void QSGGuiThreadRenderLoop::handleDeviceLoss()
void QSGGuiThreadRenderLoop::releaseSwapchain(QQuickWindow *window)
{
QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
-
- // Unlike the threaded render loop, this one reuses the same rendercontext
- // for all QQuickWindows for the entire lifetime of the render loop. (and
- // even if it wouldn't, special cases like destroy() - show() on the
- // QQuickWindow still needed this)
- // Therefore the renderer, if there is one, needs to be notified about the
- // destruction of certain resources because they may be referenced from
- // per-rendercontext data structures.
- if (wd->renderer)
- wd->renderer->invalidatePipelineCacheDependency(wd->rpDescForSwapchain);
-
delete wd->rpDescForSwapchain;
wd->rpDescForSwapchain = nullptr;
delete wd->swapchain;
@@ -452,7 +442,7 @@ bool QSGGuiThreadRenderLoop::ensureRhi(QQuickWindow *window, WindowData &data)
{
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
- bool current = false;
+ bool ok = rhi != nullptr;
if (!rhi) {
// This block below handles both the initial QRhi initialization and
@@ -473,7 +463,9 @@ bool QSGGuiThreadRenderLoop::ensureRhi(QQuickWindow *window, WindowData &data)
data.rhiDeviceLost = false;
- current = true;
+ ok = true;
+ // We need to guarantee that sceneGraphInitialized is
+ // emitted with a context current, if running with OpenGL.
rhi->makeThreadLocalNativeContextCurrent();
// The sample count cannot vary between windows as we use the same
@@ -493,16 +485,8 @@ bool QSGGuiThreadRenderLoop::ensureRhi(QQuickWindow *window, WindowData &data)
data.rhiDoomed = true;
handleContextCreationFailure(window);
}
- // otherwise no error, will retry on a subsequent rendering attempt
+ // otherwise no error, just return false so that we will retry on a subsequent rendering attempt
}
- } else {
- current = true;
- // With the rhi making the (OpenGL) context current serves only one
- // purpose: to enable external OpenGL rendering connected to one of
- // the QQuickWindow signals (beforeSynchronizing, beforeRendering,
- // etc.) to function like it did on the direct OpenGL path. For our
- // own rendering this call would not be necessary.
- rhi->makeThreadLocalNativeContextCurrent();
}
if (rhi && !cd->swapchain) {
@@ -551,7 +535,7 @@ bool QSGGuiThreadRenderLoop::ensureRhi(QQuickWindow *window, WindowData &data)
window->installEventFilter(this);
}
- return current;
+ return ok;
}
void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
@@ -665,6 +649,12 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
}
}
+ // Enable external OpenGL rendering connected to one of the
+ // QQuickWindow signals (beforeSynchronizing, beforeRendering,
+ // etc.) to function like it did on the direct OpenGL path,
+ // i.e. ensure there is a context current, just in case.
+ rhi->makeThreadLocalNativeContextCurrent();
+
cd->syncSceneGraph();
if (lastDirtyWindow)
rc->endSync();
@@ -775,6 +765,7 @@ QImage QSGGuiThreadRenderLoop::grab(QQuickWindow *window)
// renderWindow() so one cannot get to grab() without having done at least
// one on-screen frame.
cd->rhi->beginFrame(cd->swapchain);
+ rhi->makeThreadLocalNativeContextCurrent(); // for custom GL rendering before/during/after sync
cd->syncSceneGraph();
cd->renderSceneGraph(window->size());
QImage image = QSGRhiSupport::instance()->grabAndBlockInCurrentFrame(rhi, cd->swapchain->currentFrameCommandBuffer());
diff --git a/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
index a715092b19..b139c3428a 100644
--- a/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgrhidistancefieldglyphcache.cpp
@@ -39,6 +39,7 @@
#include "qsgrhidistancefieldglyphcache_p.h"
#include "qsgcontext_p.h"
+#include "qsgdefaultrendercontext_p.h"
#include <QtGui/private/qdistancefield_p.h>
#include <QtCore/qelapsedtimer.h>
#include <QtQml/private/qqmlglobal_p.h>
@@ -54,11 +55,12 @@ DEFINE_BOOL_CONFIG_OPTION(qsgPreferFullSizeGlyphCacheTextures, QSG_PREFER_FULLSI
# define QSG_RHI_DISTANCEFIELD_GLYPH_CACHE_PADDING 2
#endif
-QSGRhiDistanceFieldGlyphCache::QSGRhiDistanceFieldGlyphCache(QRhi *rhi,
+QSGRhiDistanceFieldGlyphCache::QSGRhiDistanceFieldGlyphCache(QSGDefaultRenderContext *rc,
const QRawFont &font,
int renderTypeQuality)
: QSGDistanceFieldGlyphCache(font, renderTypeQuality)
- , m_rhi(rhi)
+ , m_rc(rc)
+ , m_rhi(rc->rhi())
{
// Load a pregenerated cache if the font contains one
loadPregeneratedCache(font);
@@ -66,13 +68,19 @@ QSGRhiDistanceFieldGlyphCache::QSGRhiDistanceFieldGlyphCache(QRhi *rhi,
QSGRhiDistanceFieldGlyphCache::~QSGRhiDistanceFieldGlyphCache()
{
- for (int i = 0; i < m_textures.count(); ++i)
- delete m_textures[i].texture;
+ // A plain delete should work, but just in case commitResourceUpdates was
+ // not called and something is enqueued on the update batch for a texture,
+ // defer until the end of the frame.
+ for (int i = 0; i < m_textures.count(); ++i) {
+ if (m_textures[i].texture)
+ m_textures[i].texture->deleteLater();
+ }
delete m_areaAllocator;
// should be empty, but just in case
- qDeleteAll(m_pendingDispose);
+ for (QRhiTexture *t : qAsConst(m_pendingDispose))
+ t->deleteLater();
}
void QSGRhiDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
@@ -88,8 +96,8 @@ void QSGRhiDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
int padding = QSG_RHI_DISTANCEFIELD_GLYPH_CACHE_PADDING;
QRectF boundingRect = glyphData(glyphIndex).boundingRect;
- int glyphWidth = qCeil(boundingRect.width()) + distanceFieldRadius() * 2;
- int glyphHeight = qCeil(boundingRect.height()) + distanceFieldRadius() * 2;
+ int glyphWidth = qCeil(boundingRect.width() + distanceFieldRadius() * 2);
+ int glyphHeight = qCeil(boundingRect.height() + distanceFieldRadius() * 2);
QSize glyphSize(glyphWidth + padding * 2, glyphHeight + padding * 2);
QRect alloc = m_areaAllocator->allocate(glyphSize);
@@ -100,8 +108,8 @@ void QSGRhiDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
TexCoord unusedCoord = glyphTexCoord(unusedGlyph);
QRectF unusedGlyphBoundingRect = glyphData(unusedGlyph).boundingRect;
- int unusedGlyphWidth = qCeil(unusedGlyphBoundingRect.width()) + distanceFieldRadius() * 2;
- int unusedGlyphHeight = qCeil(unusedGlyphBoundingRect.height()) + distanceFieldRadius() * 2;
+ int unusedGlyphWidth = qCeil(unusedGlyphBoundingRect.width() + distanceFieldRadius() * 2);
+ int unusedGlyphHeight = qCeil(unusedGlyphBoundingRect.height() + distanceFieldRadius() * 2);
m_areaAllocator->deallocate(QRect(unusedCoord.x - padding,
unusedCoord.y - padding,
padding * 2 + unusedGlyphWidth,
@@ -178,15 +186,13 @@ void QSGRhiDistanceFieldGlyphCache::storeGlyphs(const QList<QDistanceField> &gly
texInfo->uploads.append(QRhiTextureUploadEntry(0, 0, subresDesc));
}
- if (!m_resourceUpdates)
- m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
-
+ QRhiResourceUpdateBatch *resourceUpdates = m_rc->glyphCacheResourceUpdates();
for (int i = 0; i < glyphs.size(); ++i) {
TextureInfo *texInfo = m_glyphsTexture.value(glyphs.at(i).glyph());
if (!texInfo->uploads.isEmpty()) {
QRhiTextureUploadDescription desc;
desc.setEntries(texInfo->uploads.cbegin(), texInfo->uploads.cend());
- m_resourceUpdates->uploadTexture(texInfo->texture, desc);
+ resourceUpdates->uploadTexture(texInfo->texture, desc);
texInfo->uploads.clear();
}
}
@@ -229,12 +235,10 @@ void QSGRhiDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
texInfo->texture = m_rhi->newTexture(QRhiTexture::RED_OR_ALPHA8, QSize(width, height), 1, QRhiTexture::UsedAsTransferSource);
if (texInfo->texture->create()) {
- if (!m_resourceUpdates)
- m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
-
+ QRhiResourceUpdateBatch *resourceUpdates = m_rc->glyphCacheResourceUpdates();
QRhiTextureSubresourceUploadDescription subresDesc(pixels, width * height);
subresDesc.setSourceSize(QSize(width, height));
- m_resourceUpdates->uploadTexture(texInfo->texture, QRhiTextureUploadEntry(0, 0, subresDesc));
+ resourceUpdates->uploadTexture(texInfo->texture, QRhiTextureUploadEntry(0, 0, subresDesc));
} else {
qWarning("Failed to create distance field glyph cache");
}
@@ -257,17 +261,15 @@ void QSGRhiDistanceFieldGlyphCache::resizeTexture(TextureInfo *texInfo, int widt
updateRhiTexture(oldTexture, texInfo->texture, texInfo->size);
- if (!m_resourceUpdates)
- m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
-
+ QRhiResourceUpdateBatch *resourceUpdates = m_rc->glyphCacheResourceUpdates();
if (useTextureResizeWorkaround()) {
QRhiTextureSubresourceUploadDescription subresDesc(texInfo->image.constBits(),
oldWidth * oldHeight);
subresDesc.setSourceSize(QSize(oldWidth, oldHeight));
- m_resourceUpdates->uploadTexture(texInfo->texture, QRhiTextureUploadEntry(0, 0, subresDesc));
+ resourceUpdates->uploadTexture(texInfo->texture, QRhiTextureUploadEntry(0, 0, subresDesc));
texInfo->image = texInfo->image.copy(0, 0, width, height);
} else {
- m_resourceUpdates->copyTexture(texInfo->texture, oldTexture);
+ resourceUpdates->copyTexture(texInfo->texture, oldTexture);
}
m_pendingDispose.insert(oldTexture);
@@ -447,7 +449,7 @@ bool QSGRhiDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
const char *textureRecord = allocatorData;
for (int i = 0; i < textureCount; ++i, textureRecord += Qtdf::TextureRecordSize) {
- if (textureRecord + Qtdf::TextureRecordSize > qtdfTableEnd) {
+ if (qtdfTableEnd - textureRecord < Qtdf::TextureRecordSize) {
qWarning("qtdf table too small in font '%s'.",
qPrintable(font.familyName()));
return false;
@@ -463,7 +465,7 @@ bool QSGRhiDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
const char *glyphRecord = textureRecord;
for (quint32 i = 0; i < glyphCount; ++i, glyphRecord += Qtdf::GlyphRecordSize) {
- if (glyphRecord + Qtdf::GlyphRecordSize > qtdfTableEnd) {
+ if (qtdfTableEnd - glyphRecord < Qtdf:: GlyphRecordSize) {
qWarning("qtdf table too small in font '%s'.",
qPrintable(font.familyName()));
return false;
@@ -513,8 +515,8 @@ bool QSGRhiDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
int width = texInfo->allocatedArea.width();
int height = texInfo->allocatedArea.height();
- qint64 size = width * height;
- if (reinterpret_cast<const char *>(textureData + size) > qtdfTableEnd) {
+ qint64 size = qint64(width) * height;
+ if (qtdfTableEnd - reinterpret_cast<const char *>(textureData) < size) {
qWarning("qtdf table too small in font '%s'.",
qPrintable(font.familyName()));
return false;
@@ -547,14 +549,13 @@ bool QSGRhiDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
void QSGRhiDistanceFieldGlyphCache::commitResourceUpdates(QRhiResourceUpdateBatch *mergeInto)
{
- if (m_resourceUpdates) {
- mergeInto->merge(m_resourceUpdates);
- m_resourceUpdates->release();
- m_resourceUpdates = nullptr;
+ if (QRhiResourceUpdateBatch *resourceUpdates = m_rc->maybeGlyphCacheResourceUpdates()) {
+ mergeInto->merge(resourceUpdates);
+ m_rc->releaseGlyphCacheResourceUpdates();
}
// now let's assume the resource updates will be committed in this frame
- for (QRhiTexture *t : m_pendingDispose)
+ for (QRhiTexture *t : qAsConst(m_pendingDispose))
t->deleteLater(); // will be deleted after the frame is submitted -> safe
m_pendingDispose.clear();
@@ -604,7 +605,8 @@ void QSGRhiDistanceFieldGlyphCache::saveTexture(QRhiTexture *texture, const QStr
};
QRhiReadbackDescription rb(texture);
- m_resourceUpdates->readBackTexture(rb, rbResult);
+ QRhiResourceUpdateBatch *resourceUpdates = m_rc->glyphCacheResourceUpdates();
+ resourceUpdates->readBackTexture(rb, rbResult);
}
#endif
diff --git a/src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h
index 48d666e7e2..965b496e48 100644
--- a/src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h
+++ b/src/quick/scenegraph/qsgrhidistancefieldglyphcache_p.h
@@ -57,10 +57,12 @@
QT_BEGIN_NAMESPACE
+class QSGDefaultRenderContext;
+
class Q_QUICK_PRIVATE_EXPORT QSGRhiDistanceFieldGlyphCache : public QSGDistanceFieldGlyphCache
{
public:
- QSGRhiDistanceFieldGlyphCache(QRhi *rhi, const QRawFont &font, int renderTypeQuality);
+ QSGRhiDistanceFieldGlyphCache(QSGDefaultRenderContext *rc, const QRawFont &font, int renderTypeQuality);
virtual ~QSGRhiDistanceFieldGlyphCache();
void requestGlyphs(const QSet<glyph_t> &glyphs) override;
@@ -114,11 +116,11 @@ private:
return &m_textures[index];
}
+ QSGDefaultRenderContext *m_rc;
QRhi *m_rhi;
mutable int m_maxTextureSize = 0;
int m_maxTextureCount = 3;
QSGAreaAllocator *m_areaAllocator = nullptr;
- QRhiResourceUpdateBatch *m_resourceUpdates = nullptr;
QList<TextureInfo> m_textures;
QHash<glyph_t, TextureInfo *> m_glyphsTexture;
QSet<glyph_t> m_unusedGlyphs;
diff --git a/src/quick/scenegraph/qsgrhilayer.cpp b/src/quick/scenegraph/qsgrhilayer.cpp
index cef9737fd8..849ed7d49f 100644
--- a/src/quick/scenegraph/qsgrhilayer.cpp
+++ b/src/quick/scenegraph/qsgrhilayer.cpp
@@ -214,12 +214,8 @@ void QSGRhiLayer::releaseResources()
delete m_rt;
m_rt = nullptr;
- if (m_rtRp) {
- if (m_renderer)
- m_renderer->invalidatePipelineCacheDependency(m_rtRp);
- delete m_rtRp;
- m_rtRp = nullptr;
- }
+ delete m_rtRp;
+ m_rtRp = nullptr;
delete m_ds;
m_ds = nullptr;
diff --git a/src/quick/scenegraph/qsgrhishadereffectnode.cpp b/src/quick/scenegraph/qsgrhishadereffectnode.cpp
index 9868a7b0f4..5575731675 100644
--- a/src/quick/scenegraph/qsgrhishadereffectnode.cpp
+++ b/src/quick/scenegraph/qsgrhishadereffectnode.cpp
@@ -47,6 +47,7 @@
#include <QQmlFile>
#include <QFile>
#include <QFileSelector>
+#include <QMutexLocker>
QT_BEGIN_NAMESPACE
@@ -116,6 +117,12 @@ void QSGRhiShaderLinker::feedSamplers(const QSGShaderEffectNode::ShaderData &sha
const QSGShaderEffectNode::VariableData &vd(shader.varData.at(i));
if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler) {
Q_ASSERT(vd.specialType == QSGShaderEffectNode::VariableData::Source);
+
+#ifndef QT_NO_DEBUG
+ int existingBindPoint = m_samplerNameMap.value(var.name, -1);
+ Q_ASSERT(existingBindPoint < 0 || existingBindPoint == var.bindPoint);
+#endif
+
m_samplers.insert(var.bindPoint, vd.value);
m_samplerNameMap.insert(var.name, var.bindPoint);
}
@@ -124,6 +131,12 @@ void QSGRhiShaderLinker::feedSamplers(const QSGShaderEffectNode::ShaderData &sha
for (int idx : *dirtyIndices) {
const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &var(shader.shaderInfo.variables.at(idx));
const QSGShaderEffectNode::VariableData &vd(shader.varData.at(idx));
+
+#ifndef QT_NO_DEBUG
+ int existingBindPoint = m_samplerNameMap.value(var.name, -1);
+ Q_ASSERT(existingBindPoint < 0 || existingBindPoint == var.bindPoint);
+#endif
+
m_samplers.insert(var.bindPoint, vd.value);
m_samplerNameMap.insert(var.name, var.bindPoint);
}
@@ -207,7 +220,8 @@ QSGMaterialType *QSGRhiShaderMaterialTypeCache::get(const QShader &vs, const QSh
return t;
}
-static QSGRhiShaderMaterialTypeCache shaderMaterialTypeCache;
+static QHash<void *, QSGRhiShaderMaterialTypeCache> shaderMaterialTypeCache;
+static QMutex shaderMaterialTypeCacheMutex;
class QSGRhiShaderEffectMaterialShader : public QSGMaterialShader
{
@@ -576,9 +590,9 @@ void QSGRhiShaderEffectMaterial::updateTextureProviders(bool layoutChange)
}
QSGRhiShaderEffectNode::QSGRhiShaderEffectNode(QSGDefaultRenderContext *rc)
- : m_rc(rc),
- m_material(this)
+ : m_material(this)
{
+ Q_UNUSED(rc);
setFlag(UsePreprocess, true);
setMaterial(&m_material);
}
@@ -657,7 +671,12 @@ void QSGRhiShaderEffectNode::syncMaterial(SyncData *syncData)
m_material.m_fragmentShader = defaultFragmentShader;
}
- m_material.m_materialType = shaderMaterialTypeCache.get(m_material.m_vertexShader, m_material.m_fragmentShader);
+ {
+ QMutexLocker lock(&shaderMaterialTypeCacheMutex);
+ m_material.m_materialType = shaderMaterialTypeCache[syncData->materialTypeCacheKey].get(m_material.m_vertexShader,
+ m_material.m_fragmentShader);
+ }
+
m_material.m_linker.reset(m_material.m_vertexShader, m_material.m_fragmentShader);
if (m_material.m_hasCustomVertexShader) {
@@ -775,9 +794,10 @@ void QSGRhiShaderEffectNode::preprocess()
}
}
-void QSGRhiShaderEffectNode::cleanupMaterialTypeCache()
+void QSGRhiShaderEffectNode::cleanupMaterialTypeCache(void *materialTypeCacheKey)
{
- shaderMaterialTypeCache.reset();
+ QMutexLocker lock(&shaderMaterialTypeCacheMutex);
+ shaderMaterialTypeCache[materialTypeCacheKey].reset();
}
bool QSGRhiGuiThreadShaderEffectManager::hasSeparateSamplerAndTextureObjects() const
@@ -805,7 +825,12 @@ void QSGRhiGuiThreadShaderEffectManager::prepareShaderCode(ShaderInfo::Type type
const QString fn = m_fileSelector->select(QQmlFile::urlToLocalFileOrQrc(src));
const QShader s = loadShaderFromFile(fn);
if (!s.isValid()) {
- qWarning("ShaderEffect: Failed to deserialize QShader from %s", qPrintable(fn));
+ qWarning("ShaderEffect: Failed to deserialize QShader from %s. "
+ "Either the filename is incorrect, or it is not a valid .qsb file. "
+ "In Qt 6 shaders must be preprocessed using the Qt Shader Tools infrastructure. "
+ "The vertexShader and fragmentShader properties are now URLs that are expected to point to .qsb files generated by the qsb tool. "
+ "See https://doc.qt.io/qt-6/qtshadertools-index.html for more information.",
+ qPrintable(fn));
m_status = Error;
emit shaderCodePrepared(false, typeHint, src, result);
emit logAndStatusChanged();
@@ -878,3 +903,5 @@ bool QSGRhiGuiThreadShaderEffectManager::reflect(ShaderInfo *result)
}
QT_END_NAMESPACE
+
+#include "moc_qsgrhishadereffectnode_p.cpp"
diff --git a/src/quick/scenegraph/qsgrhishadereffectnode_p.h b/src/quick/scenegraph/qsgrhishadereffectnode_p.h
index 0bef0d571d..f60c5c8895 100644
--- a/src/quick/scenegraph/qsgrhishadereffectnode_p.h
+++ b/src/quick/scenegraph/qsgrhishadereffectnode_p.h
@@ -135,14 +135,13 @@ public:
void syncMaterial(SyncData *syncData) override;
void preprocess() override;
- static void cleanupMaterialTypeCache();
+ static void cleanupMaterialTypeCache(void *materialTypeCacheKey);
private Q_SLOTS:
void handleTextureChange();
void handleTextureProviderDestroyed(QObject *object);
private:
- QSGDefaultRenderContext *m_rc;
QSGRhiShaderEffectMaterial m_material;
};
diff --git a/src/quick/scenegraph/qsgrhitextureglyphcache.cpp b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
index 1ec7e0c92d..e46a2d9556 100644
--- a/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
+++ b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
@@ -38,15 +38,18 @@
****************************************************************************/
#include "qsgrhitextureglyphcache_p.h"
+#include "qsgdefaultrendercontext_p.h"
#include <qrgb.h>
#include <private/qdrawhelper_p.h>
QT_BEGIN_NAMESPACE
-QSGRhiTextureGlyphCache::QSGRhiTextureGlyphCache(QRhi *rhi, QFontEngine::GlyphFormat format, const QTransform &matrix,
+QSGRhiTextureGlyphCache::QSGRhiTextureGlyphCache(QSGDefaultRenderContext *rc,
+ QFontEngine::GlyphFormat format, const QTransform &matrix,
const QColor &color)
: QImageTextureGlyphCache(format, matrix, color),
- m_rhi(rhi)
+ m_rc(rc),
+ m_rhi(rc->rhi())
{
// Some OpenGL implementations, for instance macOS, have issues with
// GL_ALPHA render targets. Similarly, BGRA may be problematic on GLES 2.0.
@@ -56,13 +59,15 @@ QSGRhiTextureGlyphCache::QSGRhiTextureGlyphCache(QRhi *rhi, QFontEngine::GlyphFo
QSGRhiTextureGlyphCache::~QSGRhiTextureGlyphCache()
{
- if (m_resourceUpdates)
- m_resourceUpdates->release();
-
- delete m_texture;
+ // A plain delete should work, but just in case commitResourceUpdates was
+ // not called and something is enqueued on the update batch for m_texture,
+ // defer until the end of the frame.
+ if (m_texture)
+ m_texture->deleteLater();
// should be empty, but just in case
- qDeleteAll(m_pendingDispose);
+ for (QRhiTexture *t : qAsConst(m_pendingDispose))
+ t->deleteLater();
}
QRhiTexture *QSGRhiTextureGlyphCache::createEmptyTexture(QRhiTexture::Format format)
@@ -73,8 +78,7 @@ QRhiTexture *QSGRhiTextureGlyphCache::createEmptyTexture(QRhiTexture::Format for
return nullptr;
}
- if (!m_resourceUpdates)
- m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
+ QRhiResourceUpdateBatch *resourceUpdates = m_rc->glyphCacheResourceUpdates();
// The new texture must be cleared to 0 always, this cannot be avoided
// otherwise artifacts will occur around the glyphs.
@@ -85,7 +89,7 @@ QRhiTexture *QSGRhiTextureGlyphCache::createEmptyTexture(QRhiTexture::Format for
data.fill(0, m_size.width() * m_size.height() * 4);
QRhiTextureSubresourceUploadDescription subresDesc(data.constData(), data.size());
subresDesc.setSourceSize(m_size);
- m_resourceUpdates->uploadTexture(t, QRhiTextureUploadEntry(0, 0, subresDesc));
+ resourceUpdates->uploadTexture(t, QRhiTextureUploadEntry(0, 0, subresDesc));
return t;
}
@@ -116,11 +120,9 @@ void QSGRhiTextureGlyphCache::resizeTextureData(int width, int height)
if (!t)
return;
- if (!m_resourceUpdates)
- m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
-
+ QRhiResourceUpdateBatch *resourceUpdates = m_rc->glyphCacheResourceUpdates();
if (m_resizeWithTextureCopy) {
- m_resourceUpdates->copyTexture(t, m_texture);
+ resourceUpdates->copyTexture(t, m_texture);
} else {
QImageTextureGlyphCache::resizeTextureData(width, height);
QImage img = image();
@@ -128,7 +130,7 @@ void QSGRhiTextureGlyphCache::resizeTextureData(int width, int height)
QRhiTextureSubresourceUploadDescription subresDesc(img);
const QSize oldSize = m_texture->pixelSize();
subresDesc.setSourceSize(QSize(qMin(oldSize.width(), width), qMin(oldSize.height(), height)));
- m_resourceUpdates->uploadTexture(t, QRhiTextureUploadEntry(0, 0, subresDesc));
+ resourceUpdates->uploadTexture(t, QRhiTextureUploadEntry(0, 0, subresDesc));
}
m_pendingDispose.insert(m_texture);
@@ -222,18 +224,19 @@ void QSGRhiTextureGlyphCache::endFillTexture()
return;
}
- if (!m_resourceUpdates)
- m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
-
+ QRhiResourceUpdateBatch *resourceUpdates = m_rc->glyphCacheResourceUpdates();
QRhiTextureUploadDescription desc;
desc.setEntries(m_uploads.cbegin(), m_uploads.cend());
- m_resourceUpdates->uploadTexture(m_texture, desc);
+ resourceUpdates->uploadTexture(m_texture, desc);
m_uploads.clear();
}
int QSGRhiTextureGlyphCache::glyphPadding() const
{
- return 1;
+ if (m_format == QFontEngine::Format_Mono)
+ return 8;
+ else
+ return 1;
}
int QSGRhiTextureGlyphCache::maxTextureWidth() const
@@ -251,14 +254,13 @@ int QSGRhiTextureGlyphCache::maxTextureHeight() const
void QSGRhiTextureGlyphCache::commitResourceUpdates(QRhiResourceUpdateBatch *mergeInto)
{
- if (m_resourceUpdates) {
- mergeInto->merge(m_resourceUpdates);
- m_resourceUpdates->release();
- m_resourceUpdates = nullptr;
+ if (QRhiResourceUpdateBatch *resourceUpdates = m_rc->maybeGlyphCacheResourceUpdates()) {
+ mergeInto->merge(resourceUpdates);
+ m_rc->releaseGlyphCacheResourceUpdates();
}
// now let's assume the resource updates will be committed in this frame
- for (QRhiTexture *t : m_pendingDispose)
+ for (QRhiTexture *t : qAsConst(m_pendingDispose))
t->deleteLater(); // will be deleted after the frame is submitted -> safe
m_pendingDispose.clear();
diff --git a/src/quick/scenegraph/qsgrhitextureglyphcache_p.h b/src/quick/scenegraph/qsgrhitextureglyphcache_p.h
index c3964ee09f..093d5424c8 100644
--- a/src/quick/scenegraph/qsgrhitextureglyphcache_p.h
+++ b/src/quick/scenegraph/qsgrhitextureglyphcache_p.h
@@ -56,10 +56,13 @@
QT_BEGIN_NAMESPACE
+class QSGDefaultRenderContext;
+
class QSGRhiTextureGlyphCache : public QImageTextureGlyphCache
{
public:
- QSGRhiTextureGlyphCache(QRhi *rhi, QFontEngine::GlyphFormat format, const QTransform &matrix,
+ QSGRhiTextureGlyphCache(QSGDefaultRenderContext *rc,
+ QFontEngine::GlyphFormat format, const QTransform &matrix,
const QColor &color = QColor());
~QSGRhiTextureGlyphCache();
@@ -86,9 +89,9 @@ private:
void prepareGlyphImage(QImage *img);
QRhiTexture *createEmptyTexture(QRhiTexture::Format format);
+ QSGDefaultRenderContext *m_rc;
QRhi *m_rhi;
bool m_resizeWithTextureCopy;
- QRhiResourceUpdateBatch *m_resourceUpdates = nullptr;
QRhiTexture *m_texture = nullptr;
QSize m_size;
bool m_bgra = false;
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index 8a0202ede9..45639ba93b 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -285,7 +285,7 @@ public:
, stopEventProcessing(false)
{
sgrc = static_cast<QSGDefaultRenderContext *>(renderContext);
-#if defined(Q_OS_QNX) && defined(Q_PROCESSOR_X86)
+#if (defined(Q_OS_QNX) && defined(Q_PROCESSOR_X86)) || defined(Q_OS_INTEGRITY)
// The SDP 6.6.0 x86 MESA driver requires a larger stack than the default.
setStackSize(1024 * 1024);
#endif
@@ -444,11 +444,11 @@ bool QSGRenderThread::event(QEvent *e)
if (ce->window) {
if (rhi) {
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(ce->window);
- cd->rhi->makeThreadLocalNativeContextCurrent();
// The assumption is that the swapchain is usable, because on
// expose the thread starts up and renders a frame so one cannot
// get here without having done at least one on-screen frame.
cd->rhi->beginFrame(cd->swapchain);
+ cd->rhi->makeThreadLocalNativeContextCurrent(); // for custom GL rendering before/during/after sync
cd->syncSceneGraph();
sgrc->endSync();
cd->renderSceneGraph(ce->window->size());
@@ -511,27 +511,19 @@ void QSGRenderThread::invalidateGraphics(QQuickWindow *window, bool inDestructor
return;
}
-
bool wipeSG = inDestructor || !window->isPersistentSceneGraph();
bool wipeGraphics = inDestructor || (wipeSG && !window->isPersistentGraphics());
- bool current = true;
- if (rhi)
- rhi->makeThreadLocalNativeContextCurrent();
-
- if (Q_UNLIKELY(!current)) {
- qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- cleanup without an OpenGL context");
- }
+ rhi->makeThreadLocalNativeContextCurrent();
QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window);
-#if QT_CONFIG(quick_shadereffect)
- QSGRhiShaderEffectNode::cleanupMaterialTypeCache();
-#endif
-
// The canvas nodes must be cleaned up regardless if we are in the destructor..
if (wipeSG) {
dd->cleanupNodesOnShutdown();
+#if QT_CONFIG(quick_shadereffect)
+ QSGRhiShaderEffectNode::cleanupMaterialTypeCache(window);
+#endif
} else {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- persistent SG, avoiding cleanup");
return;
@@ -558,6 +550,7 @@ void QSGRenderThread::invalidateGraphics(QQuickWindow *window, bool inDestructor
}
QSGRhiSupport::instance()->destroyRhi(rhi);
rhi = nullptr;
+ dd->rhi = nullptr;
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- QRhi destroyed");
} else {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- persistent GL, avoiding cleanup");
@@ -575,7 +568,7 @@ void QSGRenderThread::sync(bool inExpose)
Q_ASSERT_X(wm->m_lockedForSync, "QSGRenderThread::sync()", "sync triggered on bad terms as gui is not already locked...");
- bool current = true;
+ bool canSync = true;
if (rhi) {
if (windowSize.width() > 0 && windowSize.height() > 0) {
// With the rhi making the (OpenGL) context current serves only one
@@ -587,12 +580,12 @@ void QSGRenderThread::sync(bool inExpose)
} else {
// Zero size windows do not initialize a swapchain and
// rendercontext. So no sync or render can be done then.
- current = false;
+ canSync = false;
}
} else {
- current = false;
+ canSync = false;
}
- if (current) {
+ if (canSync) {
QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
bool hadRenderer = d->renderer != nullptr;
// If the scene graph was touched since the last sync() make sure it sends the
@@ -774,16 +767,15 @@ void QSGRenderThread::syncAndRender()
d->animationController->unlock();
}
- bool current = true;
// Zero size windows do not initialize a swapchain and
// rendercontext. So no sync or render can be done then.
- if (d->renderer && windowSize.width() > 0 && windowSize.height() > 0 && rhi)
- rhi->makeThreadLocalNativeContextCurrent();
- else
- current = false;
+ const bool canRender = d->renderer && cd->swapchain && windowSize.width() > 0 && windowSize.height() > 0;
+
+ if (canRender) {
+ if (!syncRequested) // else this was already done in sync()
+ rhi->makeThreadLocalNativeContextCurrent();
- if (current) {
- d->renderSceneGraph(windowSize, rhi ? cd->swapchain->currentPixelSize() : QSize());
+ d->renderSceneGraph(windowSize, cd->swapchain->currentPixelSize());
if (profileFrames)
renderTime = threadTimer.nsecsElapsed();
@@ -821,7 +813,7 @@ void QSGRenderThread::syncAndRender()
// beforeFrameBegin - afterFrameEnd must always come in pairs; if there was
// no before due to 0 size then there shouldn't be an after either
- if (current)
+ if (canRender)
emit window->afterFrameEnd();
// Though it would be more correct to put this block directly after
@@ -915,6 +907,8 @@ void QSGRenderThread::ensureRhi()
}
}
if (!sgrc->rhi() && windowSize.width() > 0 && windowSize.height() > 0) {
+ // We need to guarantee that sceneGraphInitialized is emitted
+ // with a context current, if running with OpenGL.
rhi->makeThreadLocalNativeContextCurrent();
QSGDefaultRenderContext::InitParams rcParams;
rcParams.rhi = rhi;
@@ -1192,20 +1186,6 @@ void QSGThreadedRenderLoop::windowDestroyed(QQuickWindow *window)
void QSGThreadedRenderLoop::releaseSwapchain(QQuickWindow *window)
{
QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
-
- // Counterintuitive, because this is not needed under normal circumstances
- // due to the render loop using a dedicated rendercontext per thread, so
- // per window. Problem is, there are cases like calling destroy(); show();
- // on the QQuickWindow. (and we get here on SurfaceAboutToBeDestroyed, i.e.
- // from destroy())
- //
- // That means recreating the native window and all the related graphics
- // infrastructure, but the rendercontext stays around. So still have to
- // notify the renderer to invalidate the relevant objects in the caches.
- //
- if (wd->renderer)
- wd->renderer->invalidatePipelineCacheDependency(wd->rpDescForSwapchain);
-
delete wd->rpDescForSwapchain;
wd->rpDescForSwapchain = nullptr;
delete wd->swapchain;
diff --git a/src/quick/scenegraph/shaders_ng/24bittextmask.frag b/src/quick/scenegraph/shaders_ng/24bittextmask.frag
index bc3826a924..ed8da4cd30 100644
--- a/src/quick/scenegraph/shaders_ng/24bittextmask.frag
+++ b/src/quick/scenegraph/shaders_ng/24bittextmask.frag
@@ -6,8 +6,9 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
- vec4 color; // only alpha is used, but must be vec4 due to layout compat
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
+ vec4 color;
vec2 textureScale;
float dpr;
} ubuf;
diff --git a/src/quick/scenegraph/shaders_ng/32bitcolortext.frag b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag
index 63e445f90b..4198a4d339 100644
--- a/src/quick/scenegraph/shaders_ng/32bitcolortext.frag
+++ b/src/quick/scenegraph/shaders_ng/32bitcolortext.frag
@@ -6,8 +6,9 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
- vec4 color; // only alpha is used, but must be vec4 due to layout compat
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
+ vec4 color;
vec2 textureScale;
float dpr;
} ubuf;
diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask.frag b/src/quick/scenegraph/shaders_ng/8bittextmask.frag
index 6304e821ff..a06743876d 100644
--- a/src/quick/scenegraph/shaders_ng/8bittextmask.frag
+++ b/src/quick/scenegraph/shaders_ng/8bittextmask.frag
@@ -6,7 +6,8 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
diff --git a/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag
index 0d0fa1cd3a..f725cbc5e7 100644
--- a/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag
+++ b/src/quick/scenegraph/shaders_ng/8bittextmask_a.frag
@@ -6,7 +6,8 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext.frag b/src/quick/scenegraph/shaders_ng/outlinedtext.frag
index 947d161a50..e2f82d3845 100644
--- a/src/quick/scenegraph/shaders_ng/outlinedtext.frag
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext.frag
@@ -11,11 +11,12 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- // must match styledtext
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
+ // the above must stay compatible with textmask/8bittextmask
vec4 styleColor;
vec2 shift;
} ubuf;
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext.vert b/src/quick/scenegraph/shaders_ng/outlinedtext.vert
index 023f9dfdc2..4068e42f28 100644
--- a/src/quick/scenegraph/shaders_ng/outlinedtext.vert
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext.vert
@@ -10,11 +10,12 @@ layout(location = 3) out vec2 sCoordLeft;
layout(location = 4) out vec2 sCoordRight;
layout(std140, binding = 0) uniform buf {
- // must match styledtext
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
+ // the above must stay compatible with textmask/8bittextmask
vec4 styleColor;
vec2 shift;
} ubuf;
@@ -28,6 +29,6 @@ void main()
sCoordDown = (tCoord - vec2(0.0, 1.0)) * ubuf.textureScale;
sCoordLeft = (tCoord - vec2(-1.0, 0.0)) * ubuf.textureScale;
sCoordRight = (tCoord - vec2(1.0, 0.0)) * ubuf.textureScale;
- vec3 dprSnapPos = floor(vCoord.xyz * ubuf.dpr + 0.5) / ubuf.dpr;
- gl_Position = ubuf.matrix * vec4(dprSnapPos, vCoord.w);
+ vec4 xformed = ubuf.modelViewMatrix * vCoord;
+ gl_Position = ubuf.projectionMatrix * vec4(floor(xformed.xyz * ubuf.dpr + 0.5) / ubuf.dpr, xformed.w);
}
diff --git a/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag
index 5b7bd9ca82..274d891a3c 100644
--- a/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag
+++ b/src/quick/scenegraph/shaders_ng/outlinedtext_a.frag
@@ -11,11 +11,12 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- // must match styledtext
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
+ // the above must stay compatible with textmask/8bittextmask
vec4 styleColor;
vec2 shift;
} ubuf;
diff --git a/src/quick/scenegraph/shaders_ng/styledtext.frag b/src/quick/scenegraph/shaders_ng/styledtext.frag
index 0b16396037..2e380dfeae 100644
--- a/src/quick/scenegraph/shaders_ng/styledtext.frag
+++ b/src/quick/scenegraph/shaders_ng/styledtext.frag
@@ -8,7 +8,8 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
diff --git a/src/quick/scenegraph/shaders_ng/styledtext.vert b/src/quick/scenegraph/shaders_ng/styledtext.vert
index beadf07c79..271dae8d8a 100644
--- a/src/quick/scenegraph/shaders_ng/styledtext.vert
+++ b/src/quick/scenegraph/shaders_ng/styledtext.vert
@@ -7,7 +7,8 @@ layout(location = 0) out vec2 sampleCoord;
layout(location = 1) out vec2 shiftedSampleCoord;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
@@ -22,6 +23,6 @@ void main()
{
sampleCoord = tCoord * ubuf.textureScale;
shiftedSampleCoord = (tCoord - ubuf.shift) * ubuf.textureScale;
- vec3 dprSnapPos = floor(vCoord.xyz * ubuf.dpr + 0.5) / ubuf.dpr;
- gl_Position = ubuf.matrix * vec4(dprSnapPos, vCoord.w);
+ vec4 xformed = ubuf.modelViewMatrix * vCoord;
+ gl_Position = ubuf.projectionMatrix * vec4(floor(xformed.xyz * ubuf.dpr + 0.5) / ubuf.dpr, xformed.w);
}
diff --git a/src/quick/scenegraph/shaders_ng/styledtext_a.frag b/src/quick/scenegraph/shaders_ng/styledtext_a.frag
index b673137895..62e162c851 100644
--- a/src/quick/scenegraph/shaders_ng/styledtext_a.frag
+++ b/src/quick/scenegraph/shaders_ng/styledtext_a.frag
@@ -8,7 +8,8 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
diff --git a/src/quick/scenegraph/shaders_ng/textmask.frag b/src/quick/scenegraph/shaders_ng/textmask.frag
index 518d5c965f..ed8da4cd30 100644
--- a/src/quick/scenegraph/shaders_ng/textmask.frag
+++ b/src/quick/scenegraph/shaders_ng/textmask.frag
@@ -6,7 +6,8 @@ layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D _qt_texture;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
diff --git a/src/quick/scenegraph/shaders_ng/textmask.vert b/src/quick/scenegraph/shaders_ng/textmask.vert
index 9d80d5dadb..e0b3c01bce 100644
--- a/src/quick/scenegraph/shaders_ng/textmask.vert
+++ b/src/quick/scenegraph/shaders_ng/textmask.vert
@@ -6,7 +6,8 @@ layout(location = 1) in vec2 tCoord;
layout(location = 0) out vec2 sampleCoord;
layout(std140, binding = 0) uniform buf {
- mat4 matrix;
+ mat4 modelViewMatrix;
+ mat4 projectionMatrix;
vec4 color;
vec2 textureScale;
float dpr;
@@ -17,6 +18,6 @@ out gl_PerVertex { vec4 gl_Position; };
void main()
{
sampleCoord = tCoord * ubuf.textureScale;
- vec3 dprSnapPos = floor(vCoord.xyz * ubuf.dpr + 0.5) / ubuf.dpr;
- gl_Position = ubuf.matrix * vec4(dprSnapPos, vCoord.w);
+ vec4 xformed = ubuf.modelViewMatrix * vCoord;
+ gl_Position = ubuf.projectionMatrix * vec4(floor(xformed.xyz * ubuf.dpr + 0.5) / ubuf.dpr, xformed.w);
}
diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
index d97d48c78d..fc1b3aec24 100644
--- a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
+++ b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
@@ -206,13 +206,11 @@ void QSGDefaultPainterNode::updateRenderTarget()
m_image = QImage(m_textureSize, QImage::Format_ARGB32_Premultiplied);
m_image.fill(Qt::transparent);
- QSGPainterTexture *texture = new QSGPainterTexture;
- texture->setOwnsTexture(true);
- texture->setTextureSize(m_textureSize);
- if (m_texture)
- delete m_texture;
-
- m_texture = texture;
+ if (!m_texture) {
+ m_texture = new QSGPainterTexture;
+ m_texture->setOwnsTexture(true);
+ }
+ m_texture->setTextureSize(m_textureSize);
}
void QSGDefaultPainterNode::setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target)
diff --git a/src/quick/scenegraph/util/qsgplaintexture.cpp b/src/quick/scenegraph/util/qsgplaintexture.cpp
index 6e59f9e607..128094cc94 100644
--- a/src/quick/scenegraph/util/qsgplaintexture.cpp
+++ b/src/quick/scenegraph/util/qsgplaintexture.cpp
@@ -231,7 +231,12 @@ void QSGPlainTexture::commitTextureOperations(QRhi *rhi, QRhiResourceUpdateBatch
}
}
- bool needsRebuild = m_texture && m_texture->pixelSize() != m_texture_size;
+ bool needsRebuild = false;
+
+ if (m_texture && m_texture->pixelSize() != m_texture_size) {
+ m_texture->setPixelSize(m_texture_size);
+ needsRebuild = true;
+ }
if (mipmappingChanged) {
QRhiTexture::Flags f = m_texture->flags();
@@ -276,3 +281,5 @@ void QSGPlainTexture::commitTextureOperations(QRhi *rhi, QRhiResourceUpdateBatch
}
QT_END_NAMESPACE
+
+#include "moc_qsgplaintexture_p.cpp"
diff --git a/src/quick/scenegraph/util/qsgrhiatlastexture.cpp b/src/quick/scenegraph/util/qsgrhiatlastexture.cpp
index d60b77ce2e..2a7be48d3d 100644
--- a/src/quick/scenegraph/util/qsgrhiatlastexture.cpp
+++ b/src/quick/scenegraph/util/qsgrhiatlastexture.cpp
@@ -57,7 +57,7 @@ int qt_sg_envInt(const char *name, int defaultValue);
static QElapsedTimer qsg_renderer_timer;
-DEFINE_BOOL_CONFIG_OPTION(qsgDisableCompressedAtlas, QSG_DISABLE_COMPRESSED_ATLAS)
+DEFINE_BOOL_CONFIG_OPTION(qsgEnableCompressedAtlas, QSG_ENABLE_COMPRESSED_ATLAS)
namespace QSGRhiAtlasTexture
{
@@ -124,7 +124,7 @@ QSGTexture *Manager::create(const QImage &image, bool hasAlphaChannel)
QSGTexture *Manager::create(const QSGCompressedTextureFactory *factory)
{
QSGTexture *t = nullptr;
- if (qsgDisableCompressedAtlas() || !factory->textureData()->isValid())
+ if (!qsgEnableCompressedAtlas() || !factory->textureData()->isValid())
return t;
unsigned int format = factory->textureData()->glInternalFormat();
diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp
index 0573cd8966..82cdd03acc 100644
--- a/src/quick/scenegraph/util/qsgtexturematerial.cpp
+++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp
@@ -81,6 +81,10 @@ void QSGOpaqueTextureMaterialRhiShader::updateSampledImage(RenderState &state, i
Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
QSGOpaqueTextureMaterial *tx = static_cast<QSGOpaqueTextureMaterial *>(newMaterial);
QSGTexture *t = tx->texture();
+ if (!t) {
+ *texture = nullptr;
+ return;
+ }
t->setFiltering(tx->filtering());
t->setMipmapFiltering(tx->mipmapFiltering());
diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp
index 28cea4b93d..cb9102c1bd 100644
--- a/src/quick/util/qquickanimation.cpp
+++ b/src/quick/util/qquickanimation.cpp
@@ -2019,8 +2019,9 @@ void QQuickBulkValueAnimator::updateCurrentTime(int currentTime)
void QQuickBulkValueAnimator::topLevelAnimationLoopChanged()
{
- //check for new from every top-level loop (when the top level animation is started and all subsequent loops)
- if (fromIsSourced)
+ // Check for new "from" value only when animation has one loop.
+ // Otherwise use the initial "from" value for every iteration.
+ if (m_loopCount == 1 && fromIsSourced)
*fromIsSourced = false;
QAbstractAnimationJob::topLevelAnimationLoopChanged();
}
diff --git a/src/quick/util/qquickanimation_p_p.h b/src/quick/util/qquickanimation_p_p.h
index b37680e92a..56121221bf 100644
--- a/src/quick/util/qquickanimation_p_p.h
+++ b/src/quick/util/qquickanimation_p_p.h
@@ -125,7 +125,7 @@ public:
};
//animates QQuickBulkValueUpdater (assumes start and end values will be reals or compatible)
-class Q_AUTOTEST_EXPORT QQuickBulkValueAnimator : public QAbstractAnimationJob
+class Q_QUICK_AUTOTEST_EXPORT QQuickBulkValueAnimator : public QAbstractAnimationJob
{
Q_DISABLE_COPY(QQuickBulkValueAnimator)
public:
diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp
index e18e31c023..bdb47653df 100644
--- a/src/quick/util/qquickanimatorjob.cpp
+++ b/src/quick/util/qquickanimatorjob.cpp
@@ -280,6 +280,17 @@ qreal QQuickAnimatorJob::progress(int time) const
return m_easing.valueForProgress((m_duration == 0) ? qreal(1) : qreal(time) / qreal(m_duration));
}
+void QQuickAnimatorJob::boundValue()
+{
+ qreal rangeMin = m_from;
+ qreal rangeMax = m_to;
+ if (m_from > m_to) {
+ rangeMax = m_from;
+ rangeMin = m_to;
+ }
+ m_value = qBound(rangeMin, m_value, rangeMax);
+}
+
qreal QQuickAnimatorJob::value() const
{
qreal value = m_to;
@@ -383,11 +394,12 @@ void QQuickTransformAnimatorJob::Helper::sync()
wasSynced = true;
}
+ // We update the node before checking on dirty, as the node might have changed without the animator running
+ node = d->itemNode();
+
if (dirty == 0)
return;
- node = d->itemNode();
-
if (dirty & QQuickItemPrivate::Position) {
dx = item->x();
dy = item->y();
diff --git a/src/quick/util/qquickanimatorjob_p.h b/src/quick/util/qquickanimatorjob_p.h
index 2aa0e1a380..171c0b302f 100644
--- a/src/quick/util/qquickanimatorjob_p.h
+++ b/src/quick/util/qquickanimatorjob_p.h
@@ -120,10 +120,16 @@ public:
virtual void setTarget(QQuickItem *target);
QQuickItem *target() const { return m_target; }
- void setFrom(qreal from) { m_from = from; }
+ void setFrom(qreal from) {
+ m_from = from;
+ boundValue();
+ }
qreal from() const { return m_from; }
- void setTo(qreal to) { m_to = to; }
+ void setTo(qreal to) {
+ m_to = to;
+ boundValue();
+ }
qreal to() const { return m_to; }
void setDuration(int duration) { m_duration = duration; }
@@ -169,6 +175,7 @@ protected:
void debugAnimation(QDebug d) const override;
qreal progress(int time) const;
+ void boundValue();
QPointer<QQuickItem> m_target;
QQuickAnimatorController *m_controller;
diff --git a/src/quick/util/qquickapplication.cpp b/src/quick/util/qquickapplication.cpp
index fa67823824..fdd9cb2f13 100644
--- a/src/quick/util/qquickapplication.cpp
+++ b/src/quick/util/qquickapplication.cpp
@@ -50,11 +50,177 @@
QT_BEGIN_NAMESPACE
-/*
- This object and its properties are documented as part of the Qt object,
- in qqmlengine.cpp
+/*!
+ \qmltype Application
+ \instantiates QQuickApplication
+ \inqmlmodule QtQuick
+ //! once exposed: \inherits CoreApplication?
+ //! TODO: \ingroup ?
+
+ \brief Provides access to global application
+ state properties shared by many QML components.
+
+ The Application singleton exposes a subset of QApplication's properties to
+ QML applications.
+
+ It also provides an aboutToQuit() signal, which is the same as
+ QCoreApplication::aboutToQuit().
+
+ \qml
+ import QtQuick
+
+ Window {
+ id: root
+ visible: true
+ width: 800
+ height: 680
+
+ title: `${Application.name} (${Application.version})`
+
+ Connections {
+ target: Application
+ function onAboutToQuit() {
+ console.log("Bye!")
+ }
+ }
+ }
+ \endqml
+
+ \sa SystemPalette
+*/
+
+/*!
+ \qmlproperty bool Application::active
+ \deprecated [5.2]
+
+ Returns whether the application is active.
+ Use Application.state == Qt.ApplicationActive instead
+*/
+
+/*!
+ \qmlproperty Qt::ApplicationState Application::state
+
+ This property represents the current state of the application.
+
+ \qml
+ Timer {
+ interval: 1000; repeat: true
+ active: Application.state === Qt.Qt.ApplicationActive
+ onTriggered: imageFetcher.fetchLatestImages()
+ }
+ \endqml
+*/
+
+/*!
+ \qmlproperty Qt::LayoutDirection Application::layoutDirection
+
+ This read-only property can be used to query the default layout
+ direction of the application. On system start-up, the default layout
+ direction depends on the application's language. The property has a
+ value of \c Qt.RightToLeft in locales where text and graphic elements
+ are read from right to left, and \c Qt.LeftToRight where the reading
+ direction flows from left to right. You can bind to this property to
+ customize your application layouts to support both layout directions.
+
+ \qml
+ RowLayout {
+ layoutDirection: Application.layoutDirection
+ }
+ \endqml
+*/
+
+/*!
+ \qmlproperty bool Application::supportsMultipleWindows
+
+ Returns \c true if the platform supports multiple windows. Some embedded
+ platforms do not support multiple windows, for example.
+ */
+
+/*!
+ \qmlproperty QFont Application::font
+ Returns the default application font as returned by
+ \l QGuiApplication::font().
*/
+
+/*!
+ \qmlproperty QString Application::displayName
+
+ This property represents the application display name set on the
+ QGuiApplication instance. This property can be written to in order to set
+ the application display name.
+
+ \qml
+ Binding {
+ target: Application
+ property: "displayName"
+ value: "My Awesome Application"
+ }
+ \endqml
+*/
+
+/*!
+ \qmlproperty QQmlListProperty<QQuickScreenInfo> Application::screens
+
+ An array containing the descriptions of all connected screens. The
+ elements of the array are objects with the same properties as the
+ \l{Screen} attached object. In practice the array corresponds to the screen
+ list returned by QGuiApplication::screens(). In addition to examining
+ properties like name, width, height, etc., the array elements can also be
+ assigned to the screen property of Window items, thus serving as an
+ alternative to the C++ side's QWindow::setScreen().
+
+ \sa Screen, Window, {Window::screen}{Window.screen}
+*/
+
+/* The following properties are from QQmlApplication.
+ ### Document those in QQmlApplication instead once it is exposed
+*/
+
+/*!
+ \qmlproperty QStringList Application::arguments
+
+ This is a string list of the arguments the executable was invoked with.
+ */
+
+/*!
+ \qmlproperty QString Application::name
+
+ This is the application name set on the QCoreApplication instance. This
+ property can be written to in order to set the application name.
+ */
+
+/*!
+ \qmlproperty QString Application::version
+
+ This is the application version set on the QCoreApplication instance. This
+ property can be written to in order to set the application version.
+ */
+
+/*!
+ \qmlproperty QString Application::organization
+
+ This is the organization name set on the QCoreApplication instance.
+ This property can be written to in order to set the organization name.
+ */
+
+/*!
+ \qmlproperty QString Application::domain
+
+ This is the organization domain set on the QCoreApplication instance.
+ This property can be written to in order to set the organization domain.
+ */
+
+/*!
+ \qmlsignal Application::aboutToQuit()
+
+ This signal is emitted when the application is about to quit the main
+ event loop. The signal is particularly useful if your application has to
+ do some last-second cleanup. User interaction is not possible in this state.
+ For more information, see \l {Window::closing()}{Window.closing}.
+
+ \sa QCoreApplication::aboutToQuit
+*/
QQuickApplication::QQuickApplication(QObject *parent)
: QQmlApplication(parent)
{
diff --git a/src/quick/util/qquickbehavior.cpp b/src/quick/util/qquickbehavior.cpp
index eeda609816..402c23e2af 100644
--- a/src/quick/util/qquickbehavior.cpp
+++ b/src/quick/util/qquickbehavior.cpp
@@ -109,8 +109,13 @@ public:
const QUntypedPropertyBinding &binding)
{
auto This = static_cast<UntypedProxyProperty *>(d);
- if (binding.valueMetaType() != This->type())
+ const QMetaType type = This->type();
+ if (binding.valueMetaType() != type)
return {};
+
+ // We want to notify in any case here because the target property should be set
+ // even if our proxy binding results in the default value.
+ QPropertyBindingPrivate::get(binding)->scheduleNotify();
return This->m_bindingData.setBinding(binding,
reinterpret_cast<QUntypedPropertyData *>(
This->m_storage.data()));
diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp
index 344024ab7c..b8167ebed3 100644
--- a/src/quick/util/qquickdeliveryagent.cpp
+++ b/src/quick/util/qquickdeliveryagent.cpp
@@ -39,6 +39,7 @@
#include <QtCore/qdebug.h>
#include <QtGui/private/qevent_p.h>
+#include <QtGui/private/qeventpoint_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformtheme.h>
#include <QtQml/private/qabstractanimationjob_p.h>
@@ -49,7 +50,7 @@
#include <QtQuick/private/qquickdrag_p.h>
#endif
#include <QtQuick/private/qquickprofiler_p.h>
-#include <QtQuick/qquickrendercontrol.h>
+#include <QtQuick/private/qquickrendercontrol_p.h>
#include <QtQuick/private/qquickwindow_p.h>
QT_BEGIN_NAMESPACE
@@ -81,6 +82,7 @@ void QQuickDeliveryAgentPrivate::touchToMouseEvent(QEvent::Type type, const QEve
(type == QEvent::MouseButtonRelease ? Qt::NoButton : Qt::LeftButton),
touchEvent->modifiers(), Qt::MouseEventSynthesizedByQt);
ret.setAccepted(true); // this now causes the persistent touchpoint to be accepted too
+ ret.setTimestamp(touchEvent->timestamp());
*mouseEvent = ret;
}
@@ -237,7 +239,7 @@ bool QQuickDeliveryAgentPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEve
if (item->acceptHoverEvents() && p.globalPosition() != QGuiApplicationPrivate::lastCursorPosition) {
QPointF localMousePos(qInf(), qInf());
if (QWindow *w = item->window())
- localMousePos = item->mapFromScene(w->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint()));
+ localMousePos = item->mapFromScene(w->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition));
QMouseEvent mm(QEvent::MouseMove, localMousePos, QGuiApplicationPrivate::lastCursorPosition,
Qt::NoButton, Qt::NoButton, event.modifiers());
QCoreApplication::sendEvent(item, &mm);
@@ -316,7 +318,7 @@ void QQuickDeliveryAgentPrivate::translateTouchEvent(QTouchEvent *touchEvent)
static inline bool windowHasFocus(QQuickWindow *win)
{
const QWindow *focusWindow = QGuiApplication::focusWindow();
- return win == focusWindow || QQuickRenderControl::renderWindowFor(win) == focusWindow;
+ return win == focusWindow || QQuickRenderControlPrivate::isRenderWindowFor(win, focusWindow) || !focusWindow;
}
#ifdef Q_OS_WEBOS
@@ -399,7 +401,9 @@ void QQuickDeliveryAgentPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *
if (item != rootItem && !(options & DontChangeSubFocusItem)) {
QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
if (oldSubFocusItem) {
- QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
+ QQuickItemPrivate *priv = QQuickItemPrivate::get(oldSubFocusItem);
+ priv->focus = false;
+ priv->notifyChangeListeners(QQuickItemPrivate::Focus, &QQuickItemChangeListener::itemFocusChanged, oldSubFocusItem, reason);
changed << oldSubFocusItem;
}
@@ -415,6 +419,7 @@ void QQuickDeliveryAgentPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *
#endif
) {
itemPrivate->focus = true;
+ itemPrivate->notifyChangeListeners(QQuickItemPrivate::Focus, &QQuickItemChangeListener::itemFocusChanged, item, reason);
changed << item;
}
}
@@ -454,7 +459,7 @@ void QQuickDeliveryAgentPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *
emit rootItem->window()->focusObjectChanged(activeFocusItem);
if (!changed.isEmpty())
- notifyFocusChangesRecur(changed.data(), changed.count() - 1);
+ notifyFocusChangesRecur(changed.data(), changed.count() - 1, reason);
if (isSubsceneAgent) {
auto da = QQuickWindowPrivate::get(rootItem->window())->deliveryAgent;
qCDebug(lcFocus) << " delegating setFocusInScope to" << da;
@@ -516,14 +521,18 @@ void QQuickDeliveryAgentPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem
if (item != rootItem && !(options & DontChangeSubFocusItem)) {
QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
- QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
+ QQuickItemPrivate *priv = QQuickItemPrivate::get(oldSubFocusItem);
+ priv->focus = false;
+ priv->notifyChangeListeners(QQuickItemPrivate::Focus, &QQuickItemChangeListener::itemFocusChanged, oldSubFocusItem, reason);
changed << oldSubFocusItem;
}
QQuickItemPrivate::get(item)->updateSubFocusItem(scope, false);
} else if (!(options & DontChangeFocusProperty)) {
- QQuickItemPrivate::get(item)->focus = false;
+ QQuickItemPrivate *priv = QQuickItemPrivate::get(item);
+ priv->focus = false;
+ priv->notifyChangeListeners(QQuickItemPrivate::Focus, &QQuickItemChangeListener::itemFocusChanged, item, reason);
changed << item;
}
@@ -550,8 +559,12 @@ void QQuickDeliveryAgentPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem
emit rootItem->window()->focusObjectChanged(activeFocusItem);
if (!changed.isEmpty())
- notifyFocusChangesRecur(changed.data(), changed.count() - 1);
-
+ notifyFocusChangesRecur(changed.data(), changed.count() - 1, reason);
+ if (isSubsceneAgent) {
+ auto da = QQuickWindowPrivate::get(rootItem->window())->deliveryAgent;
+ qCDebug(lcFocus) << " delegating clearFocusInScope to" << da;
+ QQuickWindowPrivate::get(rootItem->window())->deliveryAgentPrivate()->clearFocusInScope(da->rootItem(), item, reason, options);
+ }
if (oldActiveFocusItem == activeFocusItem)
qCDebug(lcFocus) << "activeFocusItem remains" << activeFocusItem << "in" << q;
else
@@ -566,27 +579,29 @@ void QQuickDeliveryAgentPrivate::clearFocusObject()
clearFocusInScope(rootItem, QQuickItemPrivate::get(rootItem)->subFocusItem, Qt::OtherFocusReason);
}
-void QQuickDeliveryAgentPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
+void QQuickDeliveryAgentPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining, Qt::FocusReason reason)
{
QPointer<QQuickItem> item(*items);
- if (remaining)
- notifyFocusChangesRecur(items + 1, remaining - 1);
-
if (item) {
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
if (itemPrivate->notifiedFocus != itemPrivate->focus) {
itemPrivate->notifiedFocus = itemPrivate->focus;
+ itemPrivate->notifyChangeListeners(QQuickItemPrivate::Focus, &QQuickItemChangeListener::itemFocusChanged, item, reason);
emit item->focusChanged(itemPrivate->focus);
}
if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
+ itemPrivate->notifyChangeListeners(QQuickItemPrivate::Focus, &QQuickItemChangeListener::itemFocusChanged, item, reason);
emit item->activeFocusChanged(itemPrivate->activeFocus);
}
}
+
+ if (remaining)
+ notifyFocusChangesRecur(items + 1, remaining - 1, reason);
}
bool QQuickDeliveryAgentPrivate::clearHover(ulong timestamp)
@@ -598,13 +613,14 @@ bool QQuickDeliveryAgentPrivate::clearHover(ulong timestamp)
if (!window)
return false;
- const QPointF lastPos = window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
+ const QPointF lastPos = window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition);
const auto modifiers = QGuiApplication::keyboardModifiers();
const bool clearHover = true;
for (auto hoverItem : hoverItems) {
auto item = hoverItem.first;
- deliverHoverEventToItem(item, lastPos, lastPos, modifiers, timestamp, clearHover);
+ if (item)
+ deliverHoverEventToItem(item, lastPos, lastPos, modifiers, timestamp, clearHover);
}
hoverItems.clear();
@@ -960,9 +976,6 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEvent(
const QPointF &scenePos, const QPointF &lastScenePos,
Qt::KeyboardModifiers modifiers, ulong timestamp)
{
- if (!QQuickItemPrivate::get(rootItem)->subtreeHoverEnabled)
- return false;
-
// The first time this function is called, hoverItems is empty.
// We then call deliverHoverEventRecursive from the rootItem, and
// populate the list with all the children and grandchildren that
@@ -978,10 +991,19 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEvent(
// visit will still have an old hoverId. We can therefore go through the
// list at the end of this function and look for items with an old hoverId,
// remove them from the list, and update their state accordingly.
- currentHoverId++;
+ const bool subtreeHoverEnabled = QQuickItemPrivate::get(rootItem)->subtreeHoverEnabled;
const bool itemsWasHovered = !hoverItems.isEmpty();
- deliverHoverEventRecursive(rootItem, scenePos, lastScenePos, modifiers, timestamp);
+
+ if (!subtreeHoverEnabled && !itemsWasHovered)
+ return false;
+
+ currentHoverId++;
+
+ if (subtreeHoverEnabled) {
+ hoveredLeafItemFound = false;
+ deliverHoverEventRecursive(rootItem, scenePos, lastScenePos, modifiers, timestamp);
+ }
// Prune the list for items that are no longer hovered
for (auto it = hoverItems.begin(); it != hoverItems.end();) {
@@ -1012,10 +1034,11 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEvent(
accepted, recursion stops. Returns \c true in that case, or \c false if the
event is rejected.
- All items that have hover enabled (either explicitly, from
- setAcceptHoverEvents(), or implicitly by having HoverHandlers) will have
- the QQuickItemPrivate::hoverEnabled flag set. And all their anchestors will
- have the QQuickItemPrivate::subtreeHoverEnabledset. This function will
+ Each item that has hover enabled (from setAcceptHoverEvents()) has the
+ QQuickItemPrivate::hoverEnabled flag set. This only controls whether we
+ should send hover events to the item itself. (HoverHandlers no longer set
+ this flag.) When an item has hoverEnabled set, all its ancestors have the
+ QQuickItemPrivate::subtreeHoverEnabled set. This function will
follow the subtrees that have subtreeHoverEnabled by recursing into each
child with that flag set. And for each child (in addition to the item
itself) that also has hoverEnabled set, we call deliverHoverEventToItem()
@@ -1026,6 +1049,20 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEvent(
end up as the only one hovered. Any other HoverHandler that may be a child
of an item that is stacked underneath, will not. Note that since siblings
can overlap, there can be more than one leaf item under the mouse.
+
+ Note that HoverHandler doesn't set the hoverEnabled flag on the parent item.
+ But still, adding a HoverHandler to an item will set its subtreeHoverEnabled flag.
+ So all the propagation logic described above will otherwise be the same.
+ But the hoverEnabled flag can be used to resolve if subtreeHoverEnabled is on
+ because the application explicitly requested it (setAcceptHoverEvents()), or
+ indirectly, because the item has HoverHandlers.
+
+ For legacy reasons (Qt 6.1), as soon as we find a leaf item that has hover
+ enabled, and therefore receives the event, we stop recursing into the remaining
+ siblings (even if the event was ignored). This means that we only allow hover
+ events to propagate up the direct parent-child hierarchy, and not to siblings.
+ However, if the first candidate HoverHandler is disabled, delivery continues
+ to the next one, which may be a sibling (QTBUG-106548).
*/
bool QQuickDeliveryAgentPrivate::deliverHoverEventRecursive(
QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
@@ -1055,12 +1092,15 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEventRecursive(
// Stop propagation / recursion
return true;
}
+ if (hoveredLeafItemFound) {
+ // Don't propagate to siblings, only to ancestors
+ break;
+ }
}
// All decendants have been visited.
// Now deliver the event to the item
- if (itemPrivate->hoverEnabled)
- return deliverHoverEventToItem(item, scenePos, lastScenePos, modifiers, timestamp, false);
+ return deliverHoverEventToItem(item, scenePos, lastScenePos, modifiers, timestamp, false);
// Continue propagation / recursion
return false;
@@ -1087,11 +1127,17 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEventToItem(
qCDebug(lcHoverTrace) << "item:" << item << "scene pos:" << scenePos << "localPos:" << localPos
<< "wasHovering:" << wasHovering << "isHovering:" << isHovering;
- // Send enter/move/leave event to the item
bool accepted = false;
- if (isHovering && !clearHover) {
+
+ // Start by sending out enter/move/leave events to the item.
+ // Note that hoverEnabled only controls if we should send out hover events to the
+ // item itself. HoverHandlers are not included, and are dealt with separately below.
+ if (itemPrivate->hoverEnabled && isHovering && !clearHover) {
// Add the item to the list of hovered items (if it doesn't exist there
// from before), and update hoverId to mark that it's (still) hovered.
+ // Also set hoveredLeafItemFound, so that only propagate in a straight
+ // line towards the root from now on.
+ hoveredLeafItemFound = true;
hoverItems[item] = currentHoverId;
if (wasHovering)
accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp);
@@ -1106,6 +1152,7 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEventToItem(
if (!itemPrivate->hasPointerHandlers())
return accepted;
+ // Next, send out hover events to the hover handlers.
// If the item didn't accept the hover event, 'accepted' is now false.
// Otherwise it's true, and then it should stay the way regardless of
// whether or not the hoverhandlers themselves are hovered.
@@ -1119,6 +1166,8 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEventToItem(
for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers) {
if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h)) {
+ if (!hh->isHovered())
+ continue;
hoverEvent.setAccepted(true);
QCoreApplication::sendEvent(hh, &hoverEvent);
}
@@ -1129,11 +1178,14 @@ bool QQuickDeliveryAgentPrivate::deliverHoverEventToItem(
for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers) {
if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h)) {
+ if (!hh->enabled())
+ continue;
hoverEvent.setAccepted(true);
hh->handlePointerEvent(&hoverEvent);
if (hh->isHovered()) {
// Mark the whole item as updated, even if only the handler is
// actually in a hovered state (because of HoverHandler.margins)
+ hoveredLeafItemFound = true;
hoverItems[item] = currentHoverId;
}
}
@@ -1181,6 +1233,8 @@ bool QQuickDeliveryAgentPrivate::deliverTouchCancelEvent(QTouchEvent *event)
const_cast<QPointingDevicePrivate *>(QPointingDevicePrivate::get(event->pointingDevice()))->
sendTouchCancelEvent(event);
+ cancelTouchMouseSynthesis();
+
return true;
}
@@ -1191,6 +1245,7 @@ void QQuickDeliveryAgentPrivate::deliverDelayedTouchEvent()
// event loop recursions (e.g if it the touch starts a dnd session).
QScopedPointer<QTouchEvent> e(delayedTouch.take());
qCDebug(lcTouchCmprs) << "delivering" << e.data();
+ compressedTouchCount = 0;
deliverPointerEvent(e.data());
}
@@ -1281,6 +1336,15 @@ bool QQuickDeliveryAgentPrivate::anyPointGrabbed(const QPointerEvent *ev)
return false;
}
+bool QQuickDeliveryAgentPrivate::allPointsGrabbed(const QPointerEvent *ev)
+{
+ for (const auto &point : ev->points()) {
+ if (!ev->exclusiveGrabber(point) && ev->passiveGrabbers(point).isEmpty())
+ return false;
+ }
+ return true;
+}
+
bool QQuickDeliveryAgentPrivate::isMouseEvent(const QPointerEvent *ev)
{
switch (ev->type()) {
@@ -1347,10 +1411,40 @@ QQuickPointingDeviceExtra *QQuickDeliveryAgentPrivate::deviceExtra(const QInputD
return extra;
}
+/*!
+ \internal
+ This function is called from handleTouchEvent() in case a series of touch
+ events containing only \c Updated and \c Stationary points arrives within a
+ short period of time. (Some touchscreens are more "jittery" than others.)
+
+ It would be a waste of CPU time to deliver events and have items in the
+ scene getting modified more often than once per frame; so here we try to
+ coalesce the series of updates into a single event containing all updates
+ that occur within one frame period, and deliverDelayedTouchEvent() is
+ called from flushFrameSynchronousEvents() to send that single event. This
+ is the reason why touch compression lives here so far, instead of in a
+ lower layer: the render loop updates the scene in sync with the screen's
+ vsync, and flushFrameSynchronousEvents() is called from there (for example
+ from QSGThreadedRenderLoop::polishAndSync(), and equivalent places in other
+ render loops). It would be preferable to move this code down to a lower
+ level eventually, though, because it's not fundamentally a Qt Quick concern.
+
+ This optimization can be turned off by setting the environment variable
+ \c QML_NO_TOUCH_COMPRESSION.
+
+ Returns \c true if "done", \c false if the caller needs to finish the
+ \a event delivery.
+*/
bool QQuickDeliveryAgentPrivate::compressTouchEvent(QTouchEvent *event)
{
+ // If this is a subscene agent, don't store any events, because
+ // flushFrameSynchronousEvents() is only called on the window's DA.
+ if (isSubsceneAgent)
+ return false;
+
QEventPoint::States states = event->touchPointStates();
if (states.testFlag(QEventPoint::State::Pressed) || states.testFlag(QEventPoint::State::Released)) {
+ qCDebug(lcTouchCmprs) << "no compression" << event;
// we can only compress an event that doesn't include any pressed or released points
return false;
}
@@ -1358,7 +1452,12 @@ bool QQuickDeliveryAgentPrivate::compressTouchEvent(QTouchEvent *event)
if (!delayedTouch) {
delayedTouch.reset(new QMutableTouchEvent(event->type(), event->pointingDevice(), event->modifiers(), event->points()));
delayedTouch->setTimestamp(event->timestamp());
- qCDebug(lcTouchCmprs) << "delayed" << delayedTouch.data();
+ for (qsizetype i = 0; i < delayedTouch->pointCount(); ++i) {
+ auto &tp = delayedTouch->point(i);
+ QMutableEventPoint::from(tp).detach();
+ }
+ ++compressedTouchCount;
+ qCDebug(lcTouchCmprs) << "delayed" << compressedTouchCount << delayedTouch.data();
if (QQuickWindow *window = rootItem->window())
window->maybeUpdate();
return true;
@@ -1392,7 +1491,14 @@ bool QQuickDeliveryAgentPrivate::compressTouchEvent(QTouchEvent *event)
// TODO optimize, or move event compression elsewhere
delayedTouch.reset(new QMutableTouchEvent(event->type(), event->pointingDevice(), event->modifiers(), tpts));
delayedTouch->setTimestamp(event->timestamp());
- qCDebug(lcTouchCmprs) << "coalesced" << delayedTouch.data();
+ for (qsizetype i = 0; i < delayedTouch->pointCount(); ++i) {
+ auto &tp = delayedTouch->point(i);
+ QMutableEventPoint::from(tp).detach();
+ }
+ ++compressedTouchCount;
+ qCDebug(lcTouchCmprs) << "coalesced" << compressedTouchCount << delayedTouch.data();
+ if (QQuickWindow *window = rootItem->window())
+ window->maybeUpdate();
return true;
}
}
@@ -1690,9 +1796,17 @@ void QQuickDeliveryAgentPrivate::deliverPointerEvent(QPointerEvent *event)
if (event->isEndEvent())
deliverPressOrReleaseEvent(event, true);
- // failsafe: never allow touch->mouse synthesis to persist after release
- if (event->isEndEvent() && isTouchEvent(event))
- cancelTouchMouseSynthesis();
+ // failsafe: never allow touch->mouse synthesis to persist after all touchpoints are released,
+ // or after the touchmouse is released
+ if (isTouchEvent(event) && touchMouseId >= 0) {
+ if (static_cast<QTouchEvent *>(event)->touchPointStates() == QEventPoint::State::Released) {
+ cancelTouchMouseSynthesis();
+ } else {
+ auto touchMousePoint = event->pointById(touchMouseId);
+ if (touchMousePoint && touchMousePoint->state() == QEventPoint::State::Released)
+ cancelTouchMouseSynthesis();
+ }
+ }
eventsInDelivery.pop();
if (sceneTransform) {
@@ -1744,7 +1858,8 @@ QVector<QQuickItem *> QQuickDeliveryAgentPrivate::pointerTargets(QQuickItem *ite
for (int ii = children.count() - 1; ii >= 0; --ii) {
QQuickItem *child = children.at(ii);
auto childPrivate = QQuickItemPrivate::get(child);
- if (!child->isVisible() || !child->isEnabled() || childPrivate->culled)
+ if (!child->isVisible() || !child->isEnabled() || childPrivate->culled ||
+ (child != item && childPrivate->extra.isAllocated() && childPrivate->extra->subsceneDeliveryAgent))
continue;
if (child != item)
@@ -1833,7 +1948,7 @@ void QQuickDeliveryAgentPrivate::deliverUpdatedPoints(QPointerEvent *event)
return;
// If some points weren't grabbed, deliver only to non-grabber PointerHandlers in reverse paint order
- if (!event->allPointsGrabbed()) {
+ if (!allPointsGrabbed(event)) {
QVector<QQuickItem *> targetItems;
for (auto &point : event->points()) {
// Presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints.
@@ -1853,7 +1968,7 @@ void QQuickDeliveryAgentPrivate::deliverUpdatedPoints(QPointerEvent *event)
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
localizePointerEvent(event, item);
itemPrivate->handlePointerEvent(event, true); // avoid re-delivering to grabbers
- if (event->allPointsGrabbed())
+ if (allPointsGrabbed(event))
break;
}
}
@@ -2010,21 +2125,19 @@ void QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem(QQuickItem *item, b
qCDebug(lcTouch) << "actually delivering" << &touchEvent << " to " << item;
QCoreApplication::sendEvent(item, &touchEvent);
eventAccepted = touchEvent.isAccepted();
- } else if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) {
+ } else {
// If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
- if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
- // send mouse event
- if (deliverTouchAsMouse(item, &touchEvent))
- eventAccepted = true;
- }
+ if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents)) &&
+ !eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton))
+ deliverTouchAsMouse(item, &touchEvent);
+ return;
}
+ Q_ASSERT(item->acceptTouchEvents()); // else we would've returned early above
if (eventAccepted) {
- // If the touch was accepted (regardless by whom or in what form),
- // update accepted new points.
bool isPressOrRelease = pointerEvent->isBeginEvent() || pointerEvent->isEndEvent();
for (int i = 0; i < touchEvent.pointCount(); ++i) {
- auto &point = QMutableEventPoint::from(touchEvent.point(i));
+ auto &point = touchEvent.point(i);
// legacy-style delivery: if the item doesn't reject the event, that means it handled ALL the points
point.setAccepted();
// but don't let the root of a subscene implicitly steal the grab from some other item (such as one of its children)
@@ -2048,6 +2161,7 @@ void QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem(QQuickItem *item, b
#if QT_CONFIG(quick_draganddrop)
void QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
{
+ QObject *formerTarget = grabber->target();
grabber->resetTarget();
QQuickDragGrabber::iterator grabItem = grabber->begin();
if (grabItem != grabber->end()) {
@@ -2073,6 +2187,7 @@ void QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QE
QDragLeaveEvent leaveEvent;
for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
QCoreApplication::sendEvent(**grabItem, &leaveEvent);
+ grabber->ignoreList().clear();
return;
} else {
QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
@@ -2091,7 +2206,8 @@ void QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QE
moveEvent->buttons(),
moveEvent->modifiers());
QQuickDropEventEx::copyActions(&enterEvent, *moveEvent);
- event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent, &currentGrabItems));
+ event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent, &currentGrabItems,
+ formerTarget));
for (grabItem = grabber->begin(); grabItem != grabber->end(); ++grabItem) {
int i = currentGrabItems.indexOf(**grabItem);
@@ -2129,10 +2245,14 @@ void QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QE
e->modifiers());
QQuickDropEventEx::copyActions(&enterEvent, *e);
event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent));
+ } else {
+ grabber->ignoreList().clear();
}
}
-bool QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event, QVarLengthArray<QQuickItem*, 64> *currentGrabItems)
+bool QQuickDeliveryAgentPrivate::deliverDragEvent(
+ QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event,
+ QVarLengthArray<QQuickItem *, 64> *currentGrabItems, QObject *formerTarget)
{
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
if (!item->isVisible() || !item->isEnabled() || QQuickItemPrivate::get(item)->culled)
@@ -2140,8 +2260,13 @@ bool QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQ
QPointF p = item->mapFromScene(event->position().toPoint());
bool itemContained = item->contains(p);
- if (!itemContained && itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
- return false;
+ const int itemIndex = grabber->ignoreList().indexOf(item);
+ if (!itemContained) {
+ if (itemIndex >= 0)
+ grabber->ignoreList().remove(itemIndex);
+
+ if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape)
+ return false;
}
QDragEnterEvent enterEvent(
@@ -2157,7 +2282,7 @@ bool QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQ
for (int ii = children.count() - 1; ii >= 0; --ii) {
if (children.at(ii)->z() < 0)
continue;
- if (deliverDragEvent(grabber, children.at(ii), &enterEvent, currentGrabItems))
+ if (deliverDragEvent(grabber, children.at(ii), &enterEvent, currentGrabItems, formerTarget))
return true;
}
@@ -2171,13 +2296,24 @@ bool QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQ
}
if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
- QDragMoveEvent translatedEvent(
- p.toPoint(),
- event->possibleActions(),
- event->mimeData(),
- event->buttons(),
- event->modifiers(),
- event->type());
+ if (event->type() == QEvent::DragEnter) {
+ if (formerTarget) {
+ QQuickItem *formerTargetItem = qobject_cast<QQuickItem *>(formerTarget);
+ if (formerTargetItem && currentGrabItems) {
+ QDragLeaveEvent leaveEvent;
+ QCoreApplication::sendEvent(formerTarget, &leaveEvent);
+
+ // Remove the item from the currentGrabItems so a leave event won't be generated
+ // later on
+ currentGrabItems->removeAll(formerTarget);
+ }
+ } else if (itemIndex >= 0) {
+ return false;
+ }
+ }
+
+ QDragMoveEvent translatedEvent(p.toPoint(), event->possibleActions(), event->mimeData(),
+ event->buttons(), event->modifiers(), event->type());
QQuickDropEventEx::copyActions(&translatedEvent, *event);
translatedEvent.setAccepted(event->isAccepted());
QCoreApplication::sendEvent(item, &translatedEvent);
@@ -2188,6 +2324,8 @@ bool QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQ
grabber->grab(item);
grabber->setTarget(item);
return true;
+ } else if (itemIndex < 0) {
+ grabber->ignoreList().append(item);
}
} else {
return true;
@@ -2199,7 +2337,7 @@ bool QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQ
for (int ii = children.count() - 1; ii >= 0; --ii) {
if (children.at(ii)->z() >= 0)
continue;
- if (deliverDragEvent(grabber, children.at(ii), &enterEvent, currentGrabItems))
+ if (deliverDragEvent(grabber, children.at(ii), &enterEvent, currentGrabItems, formerTarget))
return true;
}
@@ -2263,8 +2401,11 @@ bool QQuickDeliveryAgentPrivate::sendFilteredPointerEventImpl(QPointerEvent *eve
if (filteringParent->childMouseEventFilter(receiver, &filteringParentTouchEvent)) {
qCDebug(lcTouch) << "touch event intercepted by childMouseEventFilter of " << filteringParent;
skipDelivery.append(filteringParent);
- for (auto point : filteringParentTouchEvent.points())
- event->setExclusiveGrabber(point, filteringParent);
+ for (auto point : filteringParentTouchEvent.points()) {
+ const QQuickItem *exclusiveGrabber = qobject_cast<const QQuickItem *>(event->exclusiveGrabber(point));
+ if (!exclusiveGrabber || !exclusiveGrabber->keepTouchGrab())
+ event->setExclusiveGrabber(point, filteringParent);
+ }
return true;
} else if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents)) &&
!filteringParent->acceptTouchEvents()) {
diff --git a/src/quick/util/qquickdeliveryagent_p_p.h b/src/quick/util/qquickdeliveryagent_p_p.h
index f9095158e8..ed1ec86481 100644
--- a/src/quick/util/qquickdeliveryagent_p_p.h
+++ b/src/quick/util/qquickdeliveryagent_p_p.h
@@ -76,8 +76,8 @@ struct QQuickPointingDeviceExtra {
class Q_QUICK_PRIVATE_EXPORT QQuickDeliveryAgentPrivate : public QObjectPrivate
{
- Q_DECLARE_PUBLIC(QQuickDeliveryAgent)
public:
+ Q_DECLARE_PUBLIC(QQuickDeliveryAgent)
QQuickDeliveryAgentPrivate(QQuickItem *root);
~QQuickDeliveryAgentPrivate();
@@ -95,7 +95,7 @@ public:
void setFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions = { });
void clearFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions = { });
- static void notifyFocusChangesRecur(QQuickItem **item, int remaining);
+ static void notifyFocusChangesRecur(QQuickItem **item, int remaining, Qt::FocusReason reason);
void clearFocusObject();
void updateFocusItemTransform();
@@ -116,9 +116,11 @@ public:
#if QT_CONFIG(wheelevent)
uint lastWheelEventAccepted = 0;
#endif
+ uchar compressedTouchCount = 0;
bool allowChildEventFiltering = true;
bool allowDoubleClick = true;
bool frameSynchronousHoverEnabled = true;
+ bool hoveredLeafItemFound = false;
bool isSubsceneAgent = false;
static bool subsceneAgentsExist;
@@ -170,6 +172,7 @@ public:
static void localizePointerEvent(QPointerEvent *ev, const QQuickItem *dest);
QList<QObject *> exclusiveGrabbers(QPointerEvent *ev);
static bool anyPointGrabbed(const QPointerEvent *ev);
+ static bool allPointsGrabbed(const QPointerEvent *ev);
static bool isMouseEvent(const QPointerEvent *ev);
static bool isHoverEvent(const QPointerEvent *ev);
static bool isTouchEvent(const QPointerEvent *ev);
@@ -199,7 +202,9 @@ public:
#if QT_CONFIG(quick_draganddrop)
void deliverDragEvent(QQuickDragGrabber *, QEvent *);
- bool deliverDragEvent(QQuickDragGrabber *, QQuickItem *, QDragMoveEvent *, QVarLengthArray<QQuickItem*, 64> *currentGrabItems = nullptr);
+ bool deliverDragEvent(QQuickDragGrabber *, QQuickItem *, QDragMoveEvent *,
+ QVarLengthArray<QQuickItem *, 64> *currentGrabItems = nullptr,
+ QObject *formerTarget = nullptr);
#endif
static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold = -1);
diff --git a/src/quick/util/qquickfontmetrics.cpp b/src/quick/util/qquickfontmetrics.cpp
index ed9759d25f..b365ade5db 100644
--- a/src/quick/util/qquickfontmetrics.cpp
+++ b/src/quick/util/qquickfontmetrics.cpp
@@ -280,8 +280,8 @@ qreal QQuickFontMetrics::lineWidth() const
string should be drawn.
This method is offered as an imperative alternative to the
- \l {QQuickTextMetrics::advanceWidth}{advanceWidth} property of
- \l {QQuickTextMetrics::advanceWidth}{TextMetrics}.
+ \l {QtQuick::TextMetrics::advanceWidth}{advanceWidth} property of
+ TextMetrics.
\sa {QFontMetricsF::horizontalAdvance()}, {QFontMetricsF::height()}
*/
@@ -297,8 +297,8 @@ qreal QQuickFontMetrics::advanceWidth(const QString &text) const
specified by \a text.
This method is offered as an imperative alternative to the
- \l {QQuickTextMetrics::boundingRect}{boundingRect} property of
- \l {QQuickTextMetrics::boundingRect}{TextMetrics}.
+ \l {QtQuick::TextMetrics::boundingRect}{boundingRect} property of
+ TextMetrics.
\sa {QFontMetricsF::boundingRect()}, tightBoundingRect()
*/
@@ -314,8 +314,8 @@ QRectF QQuickFontMetrics::boundingRect(const QString &text) const
string specified by \a text.
This method is offered as an imperative alternative to the
- \l {QQuickTextMetrics::tightBoundingRect}{tightBoundingRect} property of
- \l {QQuickTextMetrics::tightBoundingRect}{TextMetrics}.
+ \l {QtQuick::TextMetrics::tightBoundingRect}{tightBoundingRect} property of
+ TextMetrics.
\sa {QFontMetricsF::tightBoundingRect()}, boundingRect()
*/
@@ -338,8 +338,8 @@ QRectF QQuickFontMetrics::tightBoundingRect(const QString &text) const
\l {Qt::TextShowMnemonic}.
This method is offered as an imperative alternative to the
- \l {QQuickTextMetrics::elidedText}{elidedText} property of
- \l {QQuickTextMetrics::elidedText}{TextMetrics}.
+ \l {QtQuick::TextMetrics::elidedText}{elidedText} property of
+ TextMetrics.
\sa Qt::TextElideMode, QFontMetricsF::elidedText()
*/
diff --git a/src/quick/util/qquickimageprovider.cpp b/src/quick/util/qquickimageprovider.cpp
index b885f0406a..3edce10947 100644
--- a/src/quick/util/qquickimageprovider.cpp
+++ b/src/quick/util/qquickimageprovider.cpp
@@ -469,7 +469,7 @@ QQuickTextureFactory *QQuickImageProvider::requestTexture(const QString &id, QSi
\class QQuickAsyncImageProvider
\since 5.6
\inmodule QtQuick
- \brief The QQuickAsyncImageProvider class provides an interface for for asynchronous control of QML image requests.
+ \brief The QQuickAsyncImageProvider class provides an interface for asynchronous control of QML image requests.
See the \l {imageresponseprovider}{Image Response Provider Example} for a complete implementation.
@@ -681,14 +681,21 @@ QQuickImageResponse *QQuickImageProviderWithOptions::requestImageResponse(const
and \a options. If the calculation otherwise concludes that scaled loading
is not recommended, an invalid size is returned.
*/
-QSize QQuickImageProviderWithOptions::loadSize(const QSize &originalSize, const QSize &requestedSize, const QByteArray &format, const QQuickImageProviderOptions &options)
+QSize QQuickImageProviderWithOptions::loadSize(const QSize &originalSize, const QSize &requestedSize, const QByteArray &format, const QQuickImageProviderOptions &options,
+ qreal devicePixelRatio)
{
QSize res;
- if ((requestedSize.width() <= 0 && requestedSize.height() <= 0) || originalSize.isEmpty())
+ const bool formatIsScalable = (format == "svg" || format == "svgz" || format == "pdf");
+ const bool noRequestedSize = requestedSize.width() <= 0 && requestedSize.height() <= 0;
+ if ((noRequestedSize && !formatIsScalable) || originalSize.isEmpty())
return res;
+ // If no sourceSize was set and we're loading an SVG, ensure that we provide
+ // a default size that accounts for DPR so that the image isn't blurry.
+ if (noRequestedSize && formatIsScalable)
+ return originalSize * devicePixelRatio;
+
const bool preserveAspectCropOrFit = options.preserveAspectRatioCrop() || options.preserveAspectRatioFit();
- const bool formatIsScalable = (format == "svg" || format == "svgz" || format == "pdf");
if (!preserveAspectCropOrFit && formatIsScalable && !requestedSize.isEmpty())
return requestedSize;
diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp
index 1550e7e65d..0513b477d4 100644
--- a/src/quick/util/qquickpixmapcache.cpp
+++ b/src/quick/util/qquickpixmapcache.cpp
@@ -419,7 +419,8 @@ static void maybeRemoveAlpha(QImage *image)
static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize, int *frameCount,
const QRect &requestRegion, const QSize &requestSize, const QQuickImageProviderOptions &providerOptions,
- QQuickImageProviderOptions::AutoTransform *appliedTransform = nullptr, int frame = 0)
+ QQuickImageProviderOptions::AutoTransform *appliedTransform = nullptr, int frame = 0,
+ qreal devicePixelRatio = 1.0)
{
QImageReader imgio(dev);
if (providerOptions.autoTransform() != QQuickImageProviderOptions::UsePluginDefaultTransform)
@@ -433,7 +434,7 @@ static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *e
if (frameCount)
*frameCount = imgio.imageCount();
- QSize scSize = QQuickImageProviderWithOptions::loadSize(imgio.size(), requestSize, imgio.format(), providerOptions);
+ QSize scSize = QQuickImageProviderWithOptions::loadSize(imgio.size(), requestSize, imgio.format(), providerOptions, devicePixelRatio);
if (scSize.isValid())
imgio.setScaledSize(scSize);
if (!requestRegion.isNull())
@@ -1334,7 +1335,8 @@ void QQuickPixmapData::removeFromCache()
static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, QQmlEngine *engine, const QUrl &url,
const QRect &requestRegion, const QSize &requestSize,
- const QQuickImageProviderOptions &providerOptions, int frame, bool *ok)
+ const QQuickImageProviderOptions &providerOptions, int frame, bool *ok,
+ qreal devicePixelRatio)
{
if (url.scheme() == QLatin1String("image")) {
QSize readSize;
@@ -1424,7 +1426,8 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q
QImage image;
QQuickImageProviderOptions::AutoTransform appliedTransform = providerOptions.autoTransform();
int frameCount;
- if (readImage(url, &f, &image, &errorString, &readSize, &frameCount, requestRegion, requestSize, providerOptions, &appliedTransform, frame)) {
+ if (readImage(url, &f, &image, &errorString, &readSize, &frameCount, requestRegion, requestSize,
+ providerOptions, &appliedTransform, frame, devicePixelRatio)) {
*ok = true;
return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestRegion, requestSize,
providerOptions, appliedTransform, frame, frameCount);
@@ -1641,7 +1644,8 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QRect &reques
}
void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QRect &requestRegion, const QSize &requestSize,
- QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions, int frame, int frameCount)
+ QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions, int frame, int frameCount,
+ qreal devicePixelRatio)
{
if (d) {
d->declarativePixmaps.remove(this);
@@ -1691,7 +1695,7 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QRect &reques
if (!(options & QQuickPixmap::Asynchronous)) {
bool ok = false;
PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url));
- d = createPixmapDataSync(this, engine, url, requestRegion, requestSize, providerOptions, frame, &ok);
+ d = createPixmapDataSync(this, engine, url, requestRegion, requestSize, providerOptions, frame, &ok, devicePixelRatio);
if (ok) {
PIXMAP_PROFILE(pixmapLoadingFinished(url, QSize(width(), height())));
if (options & QQuickPixmap::Cache)
diff --git a/src/quick/util/qquickpixmapcache_p.h b/src/quick/util/qquickpixmapcache_p.h
index 87724d6210..5600443d31 100644
--- a/src/quick/util/qquickpixmapcache_p.h
+++ b/src/quick/util/qquickpixmapcache_p.h
@@ -172,7 +172,8 @@ public:
void load(QQmlEngine *, const QUrl &, const QRect &requestRegion, const QSize &requestSize);
void load(QQmlEngine *, const QUrl &, const QRect &requestRegion, const QSize &requestSize, QQuickPixmap::Options options);
void load(QQmlEngine *, const QUrl &, const QRect &requestRegion, const QSize &requestSize,
- QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions, int frame = 0, int frameCount = 1);
+ QQuickPixmap::Options options, const QQuickImageProviderOptions &providerOptions, int frame = 0, int frameCount = 1,
+ qreal devicePixelRatio = 1.0);
void clear();
void clear(QObject *);
@@ -214,7 +215,8 @@ public:
virtual QQuickTextureFactory *requestTexture(const QString &id, QSize *size, const QSize &requestedSize, const QQuickImageProviderOptions &options);
virtual QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize, const QQuickImageProviderOptions &options);
- static QSize loadSize(const QSize &originalSize, const QSize &requestedSize, const QByteArray &format, const QQuickImageProviderOptions &options);
+ static QSize loadSize(const QSize &originalSize, const QSize &requestedSize, const QByteArray &format, const QQuickImageProviderOptions &options,
+ qreal devicePixelRatio = 1.0);
static QQuickImageProviderWithOptions *checkedCast(QQuickImageProvider *provider);
};
diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp
index e7186d349e..4b20912bf6 100644
--- a/src/quick/util/qquickpropertychanges.cpp
+++ b/src/quick/util/qquickpropertychanges.cpp
@@ -240,18 +240,22 @@ public:
void QQuickPropertyChangesParser::verifyList(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
{
- if (binding->type == QV4::CompiledData::Binding::Type_Object) {
- error(compilationUnit->objectAt(binding->value.objectIndex), QQuickPropertyChanges::tr("PropertyChanges does not support creating state-specific objects."));
- return;
- }
-
- if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty
- || binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ switch (binding->type()) {
+ case QV4::CompiledData::Binding::Type_Object:
+ error(compilationUnit->objectAt(binding->value.objectIndex),
+ QQuickPropertyChanges::tr(
+ "PropertyChanges does not support creating state-specific objects."));
+ break;
+ case QV4::CompiledData::Binding::Type_GroupProperty:
+ case QV4::CompiledData::Binding::Type_AttachedProperty: {
const QV4::CompiledData::Object *subObj = compilationUnit->objectAt(binding->value.objectIndex);
const QV4::CompiledData::Binding *subBinding = subObj->bindingTable();
- for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding) {
+ for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding)
verifyList(compilationUnit, subBinding);
- }
+ break;
+ }
+ default:
+ break;
}
}
@@ -274,8 +278,9 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
QString propertyName = propertyPrefix + compilationUnit->stringAt(binding->propertyNameIndex);
- if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty
- || binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ switch (binding->type()) {
+ case QV4::CompiledData::Binding::Type_GroupProperty:
+ case QV4::CompiledData::Binding::Type_AttachedProperty: {
QString pre = propertyName + QLatin1Char('.');
const QV4::CompiledData::Object *subObj = compilationUnit->objectAt(binding->value.objectIndex);
const QV4::CompiledData::Binding *subBinding = subObj->bindingTable();
@@ -284,6 +289,9 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
}
return;
}
+ default:
+ break;
+ }
if (propertyName.count() >= 3 &&
propertyName.at(0) == QLatin1Char('o') &&
@@ -303,7 +311,8 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
}
}
- if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) {
+ if (binding->type() == QV4::CompiledData::Binding::Type_Script
+ || binding->isTranslationBinding()) {
QUrl url = QUrl();
int line = -1;
int column = -1;
@@ -327,7 +336,7 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
}
QVariant var;
- switch (binding->type) {
+ switch (binding->type()) {
case QV4::CompiledData::Binding::Type_Script:
case QV4::CompiledData::Binding::Type_Translation:
case QV4::CompiledData::Binding::Type_TranslationById:
diff --git a/src/quick/util/qquicksmoothedanimation.cpp b/src/quick/util/qquicksmoothedanimation.cpp
index 254b1af0a2..d511dc0562 100644
--- a/src/quick/util/qquicksmoothedanimation.cpp
+++ b/src/quick/util/qquicksmoothedanimation.cpp
@@ -569,4 +569,6 @@ void QQuickSmoothedAnimation::setMaximumEasingTime(int v)
QT_END_NAMESPACE
+#include "moc_qquicksmoothedanimation_p_p.cpp"
+
#include "moc_qquicksmoothedanimation_p.cpp"
diff --git a/src/quick/util/qquickstatechangescript.cpp b/src/quick/util/qquickstatechangescript.cpp
index f971d7e551..8e978a2943 100644
--- a/src/quick/util/qquickstatechangescript.cpp
+++ b/src/quick/util/qquickstatechangescript.cpp
@@ -149,8 +149,6 @@ QQuickStateActionEvent::EventType QQuickStateChangeScript::type() const
return Script;
}
-
-#include <moc_qquickstatechangescript_p.cpp>
-
QT_END_NAMESPACE
+#include <moc_qquickstatechangescript_p.cpp>
diff --git a/src/quick/util/qquickstategroup.cpp b/src/quick/util/qquickstategroup.cpp
index b86333943c..5d28c7bc0c 100644
--- a/src/quick/util/qquickstategroup.cpp
+++ b/src/quick/util/qquickstategroup.cpp
@@ -376,7 +376,19 @@ bool QQuickStateGroupPrivate::updateAutoState()
QQuickState *state = states.at(ii);
if (state->isWhenKnown()) {
if (state->isNamed()) {
- if (state->when()) {
+ bool whenValue = state->when();
+ const QQmlProperty whenProp(state, u"when"_qs);
+ const auto potentialWhenBinding = QQmlAnyBinding::ofProperty(whenProp);
+ Q_ASSERT(!potentialWhenBinding.isUntypedPropertyBinding());
+
+ // if there is a binding, the value in when might not be up-to-date at this point
+ // so we manually re-evaluate the binding
+ if (auto binding = dynamic_cast<QQmlBinding *>(potentialWhenBinding.asAbstractBinding())) {
+ if (binding->hasValidContext())
+ whenValue = binding->evaluate().toBool();
+ }
+
+ if (whenValue) {
if (stateChangeDebug())
qWarning() << "Setting auto state due to expression";
if (currentState != state->name()) {
diff --git a/src/quick/util/qquickstyledtext.cpp b/src/quick/util/qquickstyledtext.cpp
index 13ece608be..f402172891 100644
--- a/src/quick/util/qquickstyledtext.cpp
+++ b/src/quick/util/qquickstyledtext.cpp
@@ -583,10 +583,10 @@ bool QQuickStyledTextPrivate::parseFontAttributes(const QChar *&ch, const QStrin
attr = parseAttribute(ch, textIn);
if (attr.first == QLatin1String("color")) {
valid = true;
- format.setForeground(QColor(attr.second.toString()));
+ format.setForeground(QColor(attr.second));
} else if (attr.first == QLatin1String("size")) {
valid = true;
- int size = attr.second.toString().toInt();
+ int size = attr.second.toInt();
if (attr.second.at(0) == QLatin1Char('-') || attr.second.at(0) == QLatin1Char('+'))
size += 3;
if (size >= 1 && size <= 7)
diff --git a/src/quick/util/qquicktimeline.cpp b/src/quick/util/qquicktimeline.cpp
index abe6eb7261..949724e87c 100644
--- a/src/quick/util/qquicktimeline.cpp
+++ b/src/quick/util/qquicktimeline.cpp
@@ -957,3 +957,5 @@ QQuickTimeLineObject *QQuickTimeLineCallback::callbackObject() const
}
QT_END_NAMESPACE
+
+#include "moc_qquicktimeline_p_p.cpp"
diff --git a/src/quick/util/qquicktransition.cpp b/src/quick/util/qquicktransition.cpp
index 9c55454c0e..64394ee285 100644
--- a/src/quick/util/qquicktransition.cpp
+++ b/src/quick/util/qquicktransition.cpp
@@ -82,6 +82,11 @@ QT_BEGIN_NAMESPACE
values can be set to restrict the animations to only be applied when changing
from one particular state to another.
+ Top-level animations within a transition are run in parallel. To run them
+ sequentially, define them within a SequentialAnimation:
+
+ \snippet qml/transition-reversible.qml sequential animations
+
To define multiple Transitions, specify \l Item::transitions as a list:
\snippet qml/transitions-list.qml list of transitions
@@ -229,6 +234,12 @@ void QQuickTransitionInstance::stop()
m_anim->stop();
}
+void QQuickTransitionInstance::complete()
+{
+ if (m_anim)
+ m_anim->complete();
+}
+
bool QQuickTransitionInstance::isRunning() const
{
return m_anim && m_anim->state() == QAbstractAnimationJob::Running;
diff --git a/src/quick/util/qquicktransition_p.h b/src/quick/util/qquicktransition_p.h
index bfb7d75821..ed2ec705b2 100644
--- a/src/quick/util/qquicktransition_p.h
+++ b/src/quick/util/qquicktransition_p.h
@@ -73,6 +73,7 @@ public:
void start();
void stop();
+ void complete();
bool isRunning() const;
diff --git a/src/quick/util/qquickvalidator.cpp b/src/quick/util/qquickvalidator.cpp
index c309460263..7e93d87555 100644
--- a/src/quick/util/qquickvalidator.cpp
+++ b/src/quick/util/qquickvalidator.cpp
@@ -56,6 +56,8 @@ QT_BEGIN_NAMESPACE
interpret the number and will accept locale specific digits, group separators, and positive
and negative signs. In addition, IntValidator is always guaranteed to accept a number
formatted according to the "C" locale.
+
+ \sa DoubleValidator, RegularExpressionValidator, {Validating Input Text}
*/
QQuickIntValidator::QQuickIntValidator(QObject *parent)
@@ -131,6 +133,8 @@ void QQuickIntValidator::resetLocaleName()
it is also rejected. If \l notation is DoubleValidator.ScientificNotation,
and the input is not in the valid range, it is accecpted but invalid. The
value may yet become valid by changing the exponent.
+
+ \sa IntValidator, RegularExpressionValidator, {Validating Input Text}
*/
QQuickDoubleValidator::QQuickDoubleValidator(QObject *parent)
@@ -210,6 +214,8 @@ void QQuickDoubleValidator::resetLocaleName()
The RegularExpressionValidator type provides a validator, that counts as valid any string which
matches a specified regular expression.
+
+ \sa IntValidator, DoubleValidator, {Validating Input Text}
*/
/*!
\qmlproperty regularExpression QtQuick::RegularExpressionValidator::regularExpression