2022-05-13 13:12:05 +00:00
|
|
|
// Copyright (C) 2021 The Qt Company Ltd.
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
Add TapHandler.gesturePolicy: DragWithinBounds enum value; examples
On a touchscreen, right-clicking is not directly possible; so sometimes
a long-press gesture is used as a substitute. The next thing a UI
designer would want would then be a way of showing feedback that a
long-press is in progress, rather than simply waiting for the long-press
to occur and then surprising the user with some instant action.
For example, a menu might begin to open as the user holds down the
touchpoint; but before the long-press gesture is complete, the user can
simply release, to cancel the gesture and close the menu. The timeHeld
property could drive the animation, to avoid needing a separate
animation type; in fact the reason timeHeld exists is to make it easy
to emulate this sort of touch-press animation, like one that occurs on
touchscreens since Windows 7.
But after the menu is open, the user would probably expect to be able to
drag the finger to a menu item and release, to select the menu item. For
such a purpose, the existing gesture policies weren't very useful: each
of them resets the timeHeld property if the user drags beyond the drag
threshold; so if the user expects to drag and release over a menu item,
then the timeHeld property cannot drive the menu-opening animation,
because the menu would disappear as soon as the user drags a little.
So it makes more sense to have a gesturePolicy that acts like
WithinBounds, but also applies the same policy to the timeHeld property
and the longPressed signal. We don't care about the drag threshold:
if the user is holding down a finger, it's considered to be a
long-press-in-progress, regardless of how far it has moved since press
(as long as it stays within the parent's bounds).
An example of such a menu is added. The menu must have TapHandler as
its root object, because it reacts to press-and-drag within some larger
item, larger than the menu itself. For example such a menu could be
used in a canvas-like application (drawing, diagramming, dragging things
like photos or file icons, or something like that): dragging items on
the canvas is possible, but long-pressing anywhere will open a context
menu. But in this example so far, only the menu is implemented.
It's a pie menu, because those are particularly touch-friendly; but
perhaps for the mouse, a conventional context menu would be used.
[ChangeLog][QtQuick][Event Handlers] TapHandler now has one more
gesturePolicy value: DragWithinBounds; it is similar to WithinBounds,
except that timeHeld is not reset during dragging, and the longPressed
signal can be emitted regardless of the drag threshold. This is useful
for implementing press-drag-release components such as menus, while
using timeHeld to directly drive an "opening" animation.
Change-Id: I298f8b1ad8f8d7d3c241ef4fdd68e7ec8d8b5bdd
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2021-10-07 20:27:32 +00:00
|
|
|
|
|
|
|
import QtQuick
|
|
|
|
import QtQuick.Shapes
|
|
|
|
|
|
|
|
TapHandler {
|
|
|
|
property var labels: [ "upperLeft", "upperRight", "lowerRight", "lowerLeft" ]
|
|
|
|
signal triggered(string text)
|
|
|
|
|
|
|
|
id: menuTap
|
2024-01-12 23:07:31 +00:00
|
|
|
acceptedButtons: Qt.RightButton
|
Add TapHandler.gesturePolicy: DragWithinBounds enum value; examples
On a touchscreen, right-clicking is not directly possible; so sometimes
a long-press gesture is used as a substitute. The next thing a UI
designer would want would then be a way of showing feedback that a
long-press is in progress, rather than simply waiting for the long-press
to occur and then surprising the user with some instant action.
For example, a menu might begin to open as the user holds down the
touchpoint; but before the long-press gesture is complete, the user can
simply release, to cancel the gesture and close the menu. The timeHeld
property could drive the animation, to avoid needing a separate
animation type; in fact the reason timeHeld exists is to make it easy
to emulate this sort of touch-press animation, like one that occurs on
touchscreens since Windows 7.
But after the menu is open, the user would probably expect to be able to
drag the finger to a menu item and release, to select the menu item. For
such a purpose, the existing gesture policies weren't very useful: each
of them resets the timeHeld property if the user drags beyond the drag
threshold; so if the user expects to drag and release over a menu item,
then the timeHeld property cannot drive the menu-opening animation,
because the menu would disappear as soon as the user drags a little.
So it makes more sense to have a gesturePolicy that acts like
WithinBounds, but also applies the same policy to the timeHeld property
and the longPressed signal. We don't care about the drag threshold:
if the user is holding down a finger, it's considered to be a
long-press-in-progress, regardless of how far it has moved since press
(as long as it stays within the parent's bounds).
An example of such a menu is added. The menu must have TapHandler as
its root object, because it reacts to press-and-drag within some larger
item, larger than the menu itself. For example such a menu could be
used in a canvas-like application (drawing, diagramming, dragging things
like photos or file icons, or something like that): dragging items on
the canvas is possible, but long-pressing anywhere will open a context
menu. But in this example so far, only the menu is implemented.
It's a pie menu, because those are particularly touch-friendly; but
perhaps for the mouse, a conventional context menu would be used.
[ChangeLog][QtQuick][Event Handlers] TapHandler now has one more
gesturePolicy value: DragWithinBounds; it is similar to WithinBounds,
except that timeHeld is not reset during dragging, and the longPressed
signal can be emitted regardless of the drag threshold. This is useful
for implementing press-drag-release components such as menus, while
using timeHeld to directly drive an "opening" animation.
Change-Id: I298f8b1ad8f8d7d3c241ef4fdd68e7ec8d8b5bdd
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2021-10-07 20:27:32 +00:00
|
|
|
gesturePolicy: TapHandler.DragWithinBounds
|
|
|
|
onPressedChanged: if (pressed) {
|
|
|
|
impl.x = point.position.x - impl.width / 2
|
|
|
|
impl.y = point.position.y - impl.width / 2
|
|
|
|
} else {
|
|
|
|
if (impl.highlightedShape)
|
|
|
|
menuTap.triggered(impl.highlightedShape.text)
|
|
|
|
}
|
|
|
|
|
|
|
|
property Item impl: Item {
|
|
|
|
parent: menuTap.parent
|
|
|
|
width: 100
|
|
|
|
height: 100
|
2024-01-12 23:07:31 +00:00
|
|
|
// with touchscreen or stylus, long-press slowly expands the menu to size
|
|
|
|
// with mouse or touchpad right-click, it opens instantly
|
|
|
|
scale: menuTap.point.device.pointerType === PointerDevice.Generic ?
|
|
|
|
1 : Math.min(1, Math.max(0, menuTap.timeHeld * 4))
|
Add TapHandler.gesturePolicy: DragWithinBounds enum value; examples
On a touchscreen, right-clicking is not directly possible; so sometimes
a long-press gesture is used as a substitute. The next thing a UI
designer would want would then be a way of showing feedback that a
long-press is in progress, rather than simply waiting for the long-press
to occur and then surprising the user with some instant action.
For example, a menu might begin to open as the user holds down the
touchpoint; but before the long-press gesture is complete, the user can
simply release, to cancel the gesture and close the menu. The timeHeld
property could drive the animation, to avoid needing a separate
animation type; in fact the reason timeHeld exists is to make it easy
to emulate this sort of touch-press animation, like one that occurs on
touchscreens since Windows 7.
But after the menu is open, the user would probably expect to be able to
drag the finger to a menu item and release, to select the menu item. For
such a purpose, the existing gesture policies weren't very useful: each
of them resets the timeHeld property if the user drags beyond the drag
threshold; so if the user expects to drag and release over a menu item,
then the timeHeld property cannot drive the menu-opening animation,
because the menu would disappear as soon as the user drags a little.
So it makes more sense to have a gesturePolicy that acts like
WithinBounds, but also applies the same policy to the timeHeld property
and the longPressed signal. We don't care about the drag threshold:
if the user is holding down a finger, it's considered to be a
long-press-in-progress, regardless of how far it has moved since press
(as long as it stays within the parent's bounds).
An example of such a menu is added. The menu must have TapHandler as
its root object, because it reacts to press-and-drag within some larger
item, larger than the menu itself. For example such a menu could be
used in a canvas-like application (drawing, diagramming, dragging things
like photos or file icons, or something like that): dragging items on
the canvas is possible, but long-pressing anywhere will open a context
menu. But in this example so far, only the menu is implemented.
It's a pie menu, because those are particularly touch-friendly; but
perhaps for the mouse, a conventional context menu would be used.
[ChangeLog][QtQuick][Event Handlers] TapHandler now has one more
gesturePolicy value: DragWithinBounds; it is similar to WithinBounds,
except that timeHeld is not reset during dragging, and the longPressed
signal can be emitted regardless of the drag threshold. This is useful
for implementing press-drag-release components such as menus, while
using timeHeld to directly drive an "opening" animation.
Change-Id: I298f8b1ad8f8d7d3c241ef4fdd68e7ec8d8b5bdd
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2021-10-07 20:27:32 +00:00
|
|
|
opacity: scale * 2
|
|
|
|
visible: menuTap.pressed
|
|
|
|
property Shape highlightedShape: null
|
|
|
|
|
|
|
|
component PieSegment : Shape {
|
|
|
|
id: shape
|
|
|
|
property int orientation: Qt.TopRightCorner
|
|
|
|
property alias text: text.text
|
|
|
|
|
|
|
|
width: 100
|
|
|
|
height: 100
|
|
|
|
containsMode: Shape.FillContains
|
2024-01-10 23:05:30 +00:00
|
|
|
preferredRendererType: Shape.CurveRenderer
|
Add TapHandler.gesturePolicy: DragWithinBounds enum value; examples
On a touchscreen, right-clicking is not directly possible; so sometimes
a long-press gesture is used as a substitute. The next thing a UI
designer would want would then be a way of showing feedback that a
long-press is in progress, rather than simply waiting for the long-press
to occur and then surprising the user with some instant action.
For example, a menu might begin to open as the user holds down the
touchpoint; but before the long-press gesture is complete, the user can
simply release, to cancel the gesture and close the menu. The timeHeld
property could drive the animation, to avoid needing a separate
animation type; in fact the reason timeHeld exists is to make it easy
to emulate this sort of touch-press animation, like one that occurs on
touchscreens since Windows 7.
But after the menu is open, the user would probably expect to be able to
drag the finger to a menu item and release, to select the menu item. For
such a purpose, the existing gesture policies weren't very useful: each
of them resets the timeHeld property if the user drags beyond the drag
threshold; so if the user expects to drag and release over a menu item,
then the timeHeld property cannot drive the menu-opening animation,
because the menu would disappear as soon as the user drags a little.
So it makes more sense to have a gesturePolicy that acts like
WithinBounds, but also applies the same policy to the timeHeld property
and the longPressed signal. We don't care about the drag threshold:
if the user is holding down a finger, it's considered to be a
long-press-in-progress, regardless of how far it has moved since press
(as long as it stays within the parent's bounds).
An example of such a menu is added. The menu must have TapHandler as
its root object, because it reacts to press-and-drag within some larger
item, larger than the menu itself. For example such a menu could be
used in a canvas-like application (drawing, diagramming, dragging things
like photos or file icons, or something like that): dragging items on
the canvas is possible, but long-pressing anywhere will open a context
menu. But in this example so far, only the menu is implemented.
It's a pie menu, because those are particularly touch-friendly; but
perhaps for the mouse, a conventional context menu would be used.
[ChangeLog][QtQuick][Event Handlers] TapHandler now has one more
gesturePolicy value: DragWithinBounds; it is similar to WithinBounds,
except that timeHeld is not reset during dragging, and the longPressed
signal can be emitted regardless of the drag threshold. This is useful
for implementing press-drag-release components such as menus, while
using timeHeld to directly drive an "opening" animation.
Change-Id: I298f8b1ad8f8d7d3c241ef4fdd68e7ec8d8b5bdd
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2021-10-07 20:27:32 +00:00
|
|
|
|
|
|
|
property bool highlighted: menuTap.pressed &&
|
|
|
|
shape.contains(shape.mapFromItem(menuTap.parent, menuTap.point.position))
|
|
|
|
onHighlightedChanged: {
|
|
|
|
if (highlighted)
|
|
|
|
impl.highlightedShape = shape
|
|
|
|
else if (impl.highlightedShape === shape)
|
|
|
|
impl.highlightedShape = null
|
|
|
|
}
|
|
|
|
|
|
|
|
ShapePath {
|
|
|
|
fillColor: highlighted ? "darkturquoise" : "aliceblue"
|
|
|
|
PathSvg {
|
|
|
|
id: svgPath
|
|
|
|
path: switch (orientation) {
|
|
|
|
case Qt.TopRightCorner:
|
|
|
|
return "M75,50 l 25,0 a50,50 0 0,0 -50,-50 l 0,25 a25,25 0 0,1 25,25";
|
|
|
|
case Qt.BottomRightCorner:
|
|
|
|
return "M75,50 l 25,0 a50,50 0 0,1 -50,50 l 0,-25 a25,25 0 0,0 25,-25";
|
|
|
|
case Qt.TopLeftCorner:
|
|
|
|
return "M50,25 l 0,-25 a50,50 0 0,0 -50,50 l 25,0 a25,25 0 0,1 25,-25";
|
|
|
|
case Qt.BottomLeftCorner:
|
|
|
|
return "M50,75 l 0,25 a50,50 0 0,1 -50,-50 l 25,0 a25,25 0 0,0 25,25";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Text {
|
|
|
|
id: text
|
|
|
|
anchors {
|
|
|
|
centerIn: parent
|
|
|
|
horizontalCenterOffset: switch (orientation) {
|
|
|
|
case Qt.TopRightCorner:
|
|
|
|
case Qt.BottomRightCorner:
|
|
|
|
return 25;
|
|
|
|
default:
|
|
|
|
return -25;
|
|
|
|
}
|
|
|
|
verticalCenterOffset: switch (orientation) {
|
|
|
|
case Qt.BottomLeftCorner:
|
|
|
|
case Qt.BottomRightCorner:
|
|
|
|
return 25;
|
|
|
|
default:
|
|
|
|
return -25;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
horizontalAlignment: Text.AlignHCenter
|
|
|
|
rotation: switch (orientation) {
|
|
|
|
case Qt.TopRightCorner:
|
|
|
|
case Qt.BottomLeftCorner:
|
|
|
|
return 45;
|
|
|
|
default:
|
|
|
|
return -45;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PieSegment {
|
|
|
|
orientation: Qt.TopLeftCorner
|
|
|
|
text: labels[0]
|
|
|
|
}
|
|
|
|
PieSegment {
|
|
|
|
orientation: Qt.TopRightCorner
|
|
|
|
text: labels[1]
|
|
|
|
}
|
|
|
|
PieSegment {
|
|
|
|
orientation: Qt.BottomRightCorner
|
|
|
|
text: labels[2]
|
|
|
|
}
|
|
|
|
PieSegment {
|
|
|
|
orientation: Qt.BottomLeftCorner
|
|
|
|
text: labels[3]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|