diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2023-02-20 10:25:33 +0100 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2023-03-15 20:51:05 +0100 |
commit | a8fb5c36cafe44ed03c925842707aa8c812bae9c (patch) | |
tree | 4353a3d982f2c270683f3b11d0f73dddc8be15c1 /src | |
parent | b357aca569028e46291d0c8f223c51333d1a2367 (diff) |
doc: Add snippets and animations illustrating TapHandler.GesturePolicy
People are constantly confused by GesturePolicy and its default value,
so we really need a "glanceable" reference in the docs to show the
differences between use cases.
Also clarify the pitfalls with the default DragThreshold value.
We switch from the \value tag to a 2-column \table because the \image
would otherwise break the table, and also because it saves space and
acts as a meaningful reminder to have the animation right under the
enum value that is being documented.
Task-number: QTBUG-70397
Task-number: QTBUG-73262
Task-number: QTBUG-100534
Task-number: QTBUG-107239
Task-number: QTBUG-111310
Change-Id: I1ff45f58a8a8edf55f4a8696d881aa9e0bedcfe3
Reviewed-by: Oliver Eftevaag <oliver.eftevaag@qt.io>
(cherry picked from commit a6e196ce9cd327df53ef9b9db3020f7775ee1754)
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/quick/doc/images/pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp | bin | 0 -> 53154 bytes | |||
-rw-r--r-- | src/quick/doc/images/pointerHandlers/tapHandlerButtonWithinBounds.webp | bin | 0 -> 27426 bytes | |||
-rw-r--r-- | src/quick/doc/images/pointerHandlers/tapHandlerOverlappingButtons.webp | bin | 0 -> 51008 bytes | |||
-rw-r--r-- | src/quick/doc/snippets/pointerHandlers/tapHandlerButtonReleaseWithinBounds.qml | 98 | ||||
-rw-r--r-- | src/quick/doc/snippets/pointerHandlers/tapHandlerButtonWithinBounds.qml | 98 | ||||
-rw-r--r-- | src/quick/doc/snippets/pointerHandlers/tapHandlerOverlappingButtons.qml | 98 | ||||
-rw-r--r-- | src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc | 3 | ||||
-rw-r--r-- | src/quick/handlers/qquicktaphandler.cpp | 97 |
8 files changed, 365 insertions, 29 deletions
diff --git a/src/quick/doc/images/pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp b/src/quick/doc/images/pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp Binary files differnew file mode 100644 index 0000000000..3edab7d7a1 --- /dev/null +++ b/src/quick/doc/images/pointerHandlers/tapHandlerButtonReleaseWithinBounds.webp diff --git a/src/quick/doc/images/pointerHandlers/tapHandlerButtonWithinBounds.webp b/src/quick/doc/images/pointerHandlers/tapHandlerButtonWithinBounds.webp Binary files differnew file mode 100644 index 0000000000..05cb2f2276 --- /dev/null +++ b/src/quick/doc/images/pointerHandlers/tapHandlerButtonWithinBounds.webp diff --git a/src/quick/doc/images/pointerHandlers/tapHandlerOverlappingButtons.webp b/src/quick/doc/images/pointerHandlers/tapHandlerOverlappingButtons.webp Binary files differnew file mode 100644 index 0000000000..0455097159 --- /dev/null +++ b/src/quick/doc/images/pointerHandlers/tapHandlerOverlappingButtons.webp 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/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/concepts/inputhandlers/qtquickhandlers-index.qdoc b/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc index bf889a9066..cca6b27ecf 100644 --- a/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc +++ b/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc @@ -94,7 +94,8 @@ 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. + 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 diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index 38dd64f500..db124fb474 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -229,34 +229,75 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event) The \c gesturePolicy also affects grab behavior as described below. - \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 - \l {QPointerEvent::addPassiveGrabber()}{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 - \l {QPointerEvent::setExclusiveGrabber}{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 to take the - \l {QPointerEvent::setExclusiveGrabber}{exclusive grab} on press - and retain it until release in order to detect this gesture. + \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 + + \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) { |