Unity 8
 All Classes Functions
DragHandle.qml
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  */
16 
17 import QtQuick 2.0
18 import Ubuntu.Components 0.1
19 import Ubuntu.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: DirectionalDragArea::Leftwards
42  }
43  }
44 
45  */
46 EdgeDragArea {
47  id: dragArea
48  objectName: "dragHandle"
49 
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
56 
57  property bool stretch: false
58 
59  property alias autoCompleteDragThreshold: dragEvaluator.dragThreshold
60 
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  }
69 
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  }
85 
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  }
97 
98  // Private stuff
99  QtObject {
100  id: d
101  property var previousStatus: DirectionalDragArea.WaitingForTouch
102  property real startValue
103  property real minValue: Direction.isPositive(direction) ? startValue
104  : startValue - maxTotalDragDistance
105  property real maxValue: Direction.isPositive(direction) ? startValue + maxTotalDragDistance
106  : startValue
107 
108  property var dragParent: dragArea.parent
109 
110  // The property of DragHandle's parent that will be modified
111  property string targetProp: {
112  if (stretch) {
113  Direction.isHorizontal(direction) ? "width" : "height";
114  } else {
115  Direction.isHorizontal(direction) ? "x" : "y";
116  }
117  }
118 
119  function limitMovement(inputStep) {
120  var targetValue = MathUtils.clamp(dragParent[targetProp] + inputStep, minValue, maxValue);
121  var step = targetValue - dragParent[targetProp];
122 
123  if (hintDisplacement == 0) {
124  return step;
125  }
126 
127  // we should not go behind hintingAnimation's current value
128  if (Direction.isPositive(direction)) {
129  if (dragParent[targetProp] + step < hintingAnimation.targetValue) {
130  step = hintingAnimation.targetValue - dragParent[targetProp];
131  }
132  } else {
133  if (dragParent[targetProp] + step > hintingAnimation.targetValue) {
134  step = hintingAnimation.targetValue - dragParent[targetProp];
135  }
136  }
137 
138  return step;
139  }
140 
141  function onFinishedRecognizedGesture() {
142  if (dragEvaluator.shouldAutoComplete()) {
143  completeDrag();
144  } else {
145  rollbackDrag();
146  }
147  }
148 
149  function completeDrag() {
150  if (dragParent.shown) {
151  dragParent.hide();
152  } else {
153  dragParent.show();
154  }
155  }
156 
157  function rollbackDrag() {
158  if (dragParent.shown) {
159  dragParent.show();
160  } else {
161  dragParent.hide();
162  }
163  }
164  }
165 
166  property alias edgeDragEvaluator: dragEvaluator
167 
168  EdgeDragEvaluator {
169  objectName: "edgeDragEvaluator"
170  id: dragEvaluator
171  // Effectively convert distance into the drag position projected onto the gesture direction axis
172  trackedPosition: Direction.isPositive(dragArea.direction) ? sceneDistance : -sceneDistance
173  maxDragDistance: maxTotalDragDistance
174  direction: dragArea.direction
175  }
176 
177  onDistanceChanged: {
178  if (status === DirectionalDragArea.Recognized) {
179  // don't go the whole distance in order to smooth out the movement
180  var step = distance * 0.3;
181 
182  step = d.limitMovement(step);
183 
184  parent[d.targetProp] += step;
185  }
186  }
187 
188  onStatusChanged: {
189  if (status === DirectionalDragArea.WaitingForTouch) {
190  hintingAnimation.stop();
191  if (d.previousStatus === DirectionalDragArea.Recognized) {
192  d.onFinishedRecognizedGesture();
193  } else /* d.previousStatus === DirectionalDragArea.Undecided */ {
194  // Gesture was rejected.
195  d.rollbackDrag();
196  }
197  } else /* Undecided || Recognized */ {
198  if (d.previousStatus === DirectionalDragArea.WaitingForTouch) {
199  dragEvaluator.reset();
200  if (overrideStartValue !== undefined) {
201  d.startValue = overrideStartValue;
202  } else {
203  d.startValue = parent[d.targetProp];
204  }
205 
206  if (hintDisplacement > 0) {
207  hintingAnimation.targetValue = d.startValue;
208  hintingAnimation.start();
209  }
210  }
211  }
212 
213  d.previousStatus = status;
214  }
215 }