on 436b607 help sign in
unity8/qml/Components/DragHandle.qml
223 lines 7.3 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 gesture recognition by default when hinting is used as
51 // it conflicts with the hinting idea.
52 distanceThreshold: hintDisplacement > 0 ? 0 : defaultDistanceThreshold
53 maxSilenceTime: hintDisplacement > 0 ? 60*60*1000 : defaultMaxSilenceTime
54 maxDeviation: hintDisplacement > 0 ? 999999 : defaultMaxDeviation
55 compositionTime: hintDisplacement > 0 ? 0 : defaultCompositionTime
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 property var overrideStartValue: undefined
72 SmoothedAnimation {
73 id: hintingAnimation
74 target: hintingAnimation
75 property: "targetValue"
76 duration: 150
77 velocity: -1
78 to: Direction.isPositive(direction) ? d.startValue + hintDisplacement
79 : d.startValue - hintDisplacement
80 property real targetValue
81 onTargetValueChanged: {
82 if (!running) {
83 return;
84 }
86 if (Direction.isPositive(direction)) {
87 if (parent[d.targetProp] < targetValue) {
88 parent[d.targetProp] = targetValue;
89 }
90 } else {
91 if (parent[d.targetProp] > targetValue) {
92 parent[d.targetProp] = targetValue;
93 }
94 }
95 }
96 }
98 // Private stuff
99 QtObject {
100 id: d
101 property var previousStatus: DirectionalDragArea.WaitingForTouch
102 property real startValue
103 property real minValue: {
104 if (direction == Direction.Horizontal) {
105 return startValue - maxTotalDragDistance;
106 } else if (Direction.isPositive(direction)) {
107 return startValue;
108 } else {
109 return startValue - maxTotalDragDistance;
110 }
111 }
113 property real maxValue: Direction.isPositive(direction) ? startValue + maxTotalDragDistance
114 : startValue
116 property var dragParent: dragArea.parent
118 // The property of DragHandle's parent that will be modified
119 property string targetProp: {
120 if (stretch) {
121 Direction.isHorizontal(direction) ? "width" : "height";
122 } else {
123 Direction.isHorizontal(direction) ? "x" : "y";
124 }
125 }
127 function limitMovement(inputStep) {
128 var targetValue = MathUtils.clamp(dragParent[targetProp] + inputStep, minValue, maxValue);
129 var step = targetValue - dragParent[targetProp];
131 if (hintDisplacement == 0) {
132 return step;
133 }
135 // we should not go behind hintingAnimation's current value
136 if (Direction.isPositive(direction)) {
137 if (dragParent[targetProp] + step < hintingAnimation.targetValue) {
138 step = hintingAnimation.targetValue - dragParent[targetProp];
139 }
140 } else {
141 if (dragParent[targetProp] + step > hintingAnimation.targetValue) {
142 step = hintingAnimation.targetValue - dragParent[targetProp];
143 }
144 }
146 return step;
147 }
149 function onFinishedRecognizedGesture() {
150 if (dragEvaluator.shouldAutoComplete()) {
151 completeDrag();
152 } else {
153 rollbackDrag();
154 }
155 }
157 function completeDrag() {
158 if (dragParent.shown) {
159 dragParent.hide();
160 } else {
161 dragParent.show();
162 }
163 }
165 function rollbackDrag() {
166 if (dragParent.shown) {
167 dragParent.show();
168 } else {
169 dragParent.hide();
170 }
171 }
172 }
174 property alias edgeDragEvaluator: dragEvaluator
176 EdgeDragEvaluator {
177 objectName: "edgeDragEvaluator"
178 id: dragEvaluator
179 // Effectively convert distance into the drag position projected onto the gesture direction axis
180 trackedPosition: Direction.isPositive(dragArea.direction) ? sceneDistance : -sceneDistance
181 maxDragDistance: maxTotalDragDistance
182 direction: dragArea.direction
183 }
185 onDistanceChanged: {
186 if (status === DirectionalDragArea.Recognized) {
187 // don't go the whole distance in order to smooth out the movement
188 var step = distance * 0.3;
190 step = d.limitMovement(step);
192 parent[d.targetProp] += step;
193 }
194 }
196 onStatusChanged: {
197 if (status === DirectionalDragArea.WaitingForTouch) {
198 hintingAnimation.stop();
199 if (d.previousStatus === DirectionalDragArea.Recognized) {
200 d.onFinishedRecognizedGesture();
201 } else /* d.previousStatus === DirectionalDragArea.Undecided */ {
202 // Gesture was rejected.
203 d.rollbackDrag();
204 }
205 } else /* Undecided || Recognized */ {
206 if (d.previousStatus === DirectionalDragArea.WaitingForTouch) {
207 dragEvaluator.reset();
208 if (overrideStartValue !== undefined) {
209 d.startValue = overrideStartValue;
210 } else {
211 d.startValue = parent[d.targetProp];
212 }
214 if (hintDisplacement > 0) {
215 hintingAnimation.targetValue = d.startValue;
216 hintingAnimation.start();
217 }
218 }
219 }
221 d.previousStatus = status;
222 }