on 3e61bb2 help sign in
unity8/Components/DragHandle.qml
209 lines 6.9 kB history raw
1/*
2 * Copyright (C) 2013 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 */
17import QtQuick 2.0
18import Ubuntu.Components 0.1
19import Ubuntu.Gestures 0.1
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.
25 This example shows a DragHandle placed on the right corner of a Showable, used
26 to slide it away, off the screen.
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)
41 direction: DirectionalDragArea::Leftwards
42 }
43 }
45 */
46EdgeDragArea {
47 id: dragArea
48 objectName: "dragHandle"
50 // Disable most of the gesture recognition parameters by default when hinting is used as
51 // it conflicts with the hinting idea.
52 // The only part we keep in this situation is that it must be a single-finger gesture.
53 distanceThreshold: hintDisplacement > 0 ? 0 : defaultDistanceThreshold
54 maxSilenceTime: hintDisplacement > 0 ? 60*60*1000 : defaultMaxSilenceTime
55 maxDeviation: hintDisplacement > 0 ? 999999 : defaultMaxDeviation
57 property bool stretch: false
59 property alias autoCompleteDragThreshold: dragEvaluator.dragThreshold
61 // How far you can drag
62 property real maxTotalDragDistance: {
63 if (stretch) {
64 0; // not enough context information to set a sensible default
65 } else {
66 Direction.isHorizontal(direction) ? parent.width : parent.height;
67 }
68 }
70 property real hintDisplacement: 0
71 SmoothedAnimation {
72 id: hintingAnimation
73 target: hintingAnimation
74 property: "targetValue"
75 duration: 150
76 velocity: -1
77 to: Direction.isPositive(direction) ? d.startValue + hintDisplacement
78 : d.startValue - hintDisplacement
79 property real targetValue
80 onTargetValueChanged: {
81 if (!running) {
82 return;
83 }
85 if (Direction.isPositive(direction)) {
86 if (parent[d.targetProp] < targetValue) {
87 parent[d.targetProp] = targetValue;
88 }
89 } else {
90 if (parent[d.targetProp] > targetValue) {
91 parent[d.targetProp] = targetValue;
92 }
93 }
94 }
95 }
97 // Private stuff
98 QtObject {
99 id: d
100 property var previousStatus: undefined
101 property real startValue
102 property real minValue: Direction.isPositive(direction) ? startValue
103 : startValue - maxTotalDragDistance
104 property real maxValue: Direction.isPositive(direction) ? startValue + maxTotalDragDistance
105 : startValue
107 property var dragParent: dragArea.parent
109 // The property of DragHandle's parent that will be modified
110 property string targetProp: {
111 if (stretch) {
112 Direction.isHorizontal(direction) ? "width" : "height";
113 } else {
114 Direction.isHorizontal(direction) ? "x" : "y";
115 }
116 }
118 function limitMovement(inputStep) {
119 var targetValue = MathUtils.clamp(dragParent[targetProp] + inputStep, minValue, maxValue);
120 var step = targetValue - dragParent[targetProp];
122 if (hintDisplacement == 0) {
123 return step;
124 }
126 // we should not go behind hintingAnimation's current value
127 if (Direction.isPositive(direction)) {
128 if (dragParent[targetProp] + step < hintingAnimation.targetValue) {
129 step = hintingAnimation.targetValue - dragParent[targetProp];
130 }
131 } else {
132 if (dragParent[targetProp] + step > hintingAnimation.targetValue) {
133 step = hintingAnimation.targetValue - dragParent[targetProp];
134 }
135 }
137 return step;
138 }
140 function onFinishedRecognizedGesture() {
141 if (dragEvaluator.shouldAutoComplete()) {
142 completeDrag();
143 } else {
144 rollbackDrag();
145 }
146 }
148 function completeDrag() {
149 if (dragParent.shown) {
150 dragParent.hide();
151 } else {
152 dragParent.show();
153 }
154 }
156 function rollbackDrag() {
157 if (dragParent.shown) {
158 dragParent.show();
159 } else {
160 dragParent.hide();
161 }
162 }
163 }
165 property alias edgeDragEvaluator: dragEvaluator
167 EdgeDragEvaluator {
168 objectName: "edgeDragEvaluator"
169 id: dragEvaluator
170 trackedPosition: sceneDistance
171 maxDragDistance: maxTotalDragDistance
172 direction: dragArea.direction
173 }
175 onDistanceChanged: {
176 if (status === DirectionalDragArea.Recognized) {
177 // don't go the whole distance in order to smooth out the movement
178 var step = distance * 0.3;
180 step = d.limitMovement(step);
182 parent[d.targetProp] += step;
183 }
184 }
186 onStatusChanged: {
187 if (status === DirectionalDragArea.WaitingForTouch) {
188 hintingAnimation.stop();
189 if (d.previousStatus === DirectionalDragArea.Recognized) {
190 d.onFinishedRecognizedGesture();
191 } else /* d.previousStatus === DirectionalDragArea.Undecided */ {
192 // Gesture was rejected.
193 d.rollbackDrag();
194 }
195 } else /* Undecided || Recognized */ {
196 if (d.previousStatus === DirectionalDragArea.WaitingForTouch ||
197 d.previousStatus === undefined) {
198 dragEvaluator.reset();
199 d.startValue = parent[d.targetProp];
200 }
201 if (hintDisplacement > 0) {
202 hintingAnimation.targetValue = d.startValue;
203 hintingAnimation.start();
204 }
205 }
207 d.previousStatus = status;
208 }