Lomiri
Loading...
Searching...
No Matches
DragHandle.qml
1/*
2 * Copyright (C) 2013, 2016 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.12
18import Lomiri.Components 1.3
19import Lomiri.Gestures 0.1
20
21/*
22 Put a DragHandle inside a Showable to enable the user to drag it from that handle.
23 Main use case is to drag fullscreen Showables into the screen or off the screen.
24
25 This example shows a DragHandle placed on the right corner of a Showable, used
26 to slide it away, off the screen.
27
28 Showable {
29 x: 0
30 y: 0
31 width: ... // screen width
32 height: ... // screen height
33 shown: true
34 ...
35 DragHandle {
36 anchors.right: parent.right
37 anchors.top: parent.top
38 anchors.bottom: parent.bottom
39 width: units.gu(2)
40
41 direction: SwipeArea::Leftwards
42 }
43 }
44
45 */
46SwipeArea {
47 id: dragArea
48
49 property bool stretch: false
50
51 property alias autoCompleteDragThreshold: dragEvaluator.dragThreshold
52
53 // How far you can drag
54 property real maxTotalDragDistance: {
55 if (stretch) {
56 0; // not enough context information to set a sensible default
57 } else {
58 Direction.isHorizontal(direction) ? parent.width : parent.height;
59 }
60 }
61
62 property real hintDisplacement: 0
63
64 immediateRecognition: hintDisplacement > 0
65
66 property var overrideStartValue: undefined
67 SmoothedAnimation {
68 id: hintingAnimation
69 target: hintingAnimation
70 objectName: "hintingAnimation"
71 property: "targetValue"
72 duration: 150
73 velocity: -1
74
75 to: d.incrementTargetProp ? d.startValue + hintDisplacement
76 : d.startValue - hintDisplacement
77 property real targetValue
78 onTargetValueChanged: {
79 if (!running) {
80 return;
81 }
82
83 if (d.incrementTargetProp) {
84 if (parent[d.targetProp] < targetValue) {
85 parent[d.targetProp] = targetValue;
86 }
87 } else {
88 if (parent[d.targetProp] > targetValue) {
89 parent[d.targetProp] = targetValue;
90 }
91 }
92 }
93 }
94
95 // Private stuff
96 QtObject {
97 id: d
98
99 // Whether movement along the designated direction will increment the value of the target property
100 readonly property bool incrementTargetProp: (Direction.isPositive(direction) && !dragArea.stretch)
101 || (dragArea.stretch && !d.dragParent.shown)
102
103 property real startValue
104 property real minValue: {
105 if (direction == Direction.Horizontal) {
106 return startValue - maxTotalDragDistance;
107 } else if (incrementTargetProp) {
108 return startValue;
109 } else {
110 return startValue - maxTotalDragDistance;
111 }
112 }
113
114 property real maxValue: incrementTargetProp ? startValue + maxTotalDragDistance
115 : startValue;
116
117 property var dragParent: dragArea.parent
118
119 // The property of DragHandle's parent that will be modified
120 property string targetProp: {
121 if (stretch) {
122 Direction.isHorizontal(direction) ? "width" : "height";
123 } else {
124 Direction.isHorizontal(direction) ? "x" : "y";
125 }
126 }
127
128 function limitMovement(distance) {
129 var targetValue = MathUtils.clamp(d.startValue + distance, minValue, maxValue);
130 var diff = targetValue - d.startValue;
131
132 if (hintDisplacement == 0) {
133 return diff;
134 }
135
136 // we should not go behind hintingAnimation's current value
137 if (d.incrementTargetProp) {
138 if (d.startValue + diff < hintingAnimation.targetValue) {
139 diff = hintingAnimation.targetValue - d.startValue;
140 }
141 } else {
142 if (d.startValue + diff > hintingAnimation.targetValue) {
143 diff = hintingAnimation.targetValue - d.startValue;
144 }
145 }
146
147 return diff;
148 }
149
150 function onFinishedRecognizedGesture() {
151 if (dragEvaluator.shouldAutoComplete()) {
152 completeDrag();
153 } else {
154 rollbackDrag();
155 }
156 }
157
158 function completeDrag() {
159 if (dragParent.shown) {
160 dragParent.hide();
161 } else {
162 dragParent.show();
163 }
164 }
165
166 function rollbackDrag() {
167 if (dragParent.shown) {
168 dragParent.show();
169 } else {
170 dragParent.hide();
171 }
172 }
173 }
174
175 property alias edgeDragEvaluator: dragEvaluator
176
177 EdgeDragEvaluator {
178 objectName: "edgeDragEvaluator"
179 id: dragEvaluator
180 // Effectively convert distance into the drag position projected onto the gesture direction axis
181 trackedPosition: Direction.isPositive(dragArea.direction) ? distance : -distance
182 maxDragDistance: maxTotalDragDistance
183 direction: dragArea.direction
184 }
185
186 onDistanceChanged: {
187 if (dragging) {
188 if (!Direction.isPositive(direction))
189 distance = -distance;
190
191 if (dragArea.stretch &&
192 ((!Direction.isPositive(direction) && !d.dragParent.shown)
193 ||
194 (Direction.isPositive(direction) && d.dragParent.shown))
195 )
196 {
197 // This happens when you have a stretching showable being shown from the right or
198 // top edge (and consequently being hidden when dragged towards the right/top edge)
199 // In those situations, dimension expansion/retraction happens in the opposite
200 // sign of the axis direction
201 distance = -distance;
202 }
203
204 var toAdd = d.limitMovement(distance);
205 parent[d.targetProp] = d.startValue + toAdd;
206 }
207 }
208
209 onDraggingChanged: {
210 if (dragging) {
211 dragEvaluator.reset();
212 if (overrideStartValue !== undefined) {
213 d.startValue = overrideStartValue;
214 } else {
215 d.startValue = parent[d.targetProp];
216 }
217
218 if (hintDisplacement > 0) {
219 hintingAnimation.targetValue = d.startValue;
220 hintingAnimation.start();
221 }
222 } else {
223 hintingAnimation.stop();
224 d.onFinishedRecognizedGesture();
225 }
226 }
227}