Unity 8
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.4
18 import Ubuntu.Components 1.3
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 DirectionalDragArea {
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  property: "targetValue"
71  duration: 150
72  velocity: -1
73  to: Direction.isPositive(direction) ? d.startValue + hintDisplacement
74  : d.startValue - hintDisplacement
75  property real targetValue
76  onTargetValueChanged: {
77  if (!running) {
78  return;
79  }
80 
81  if (Direction.isPositive(direction)) {
82  if (parent[d.targetProp] < targetValue) {
83  parent[d.targetProp] = targetValue;
84  }
85  } else {
86  if (parent[d.targetProp] > targetValue) {
87  parent[d.targetProp] = targetValue;
88  }
89  }
90  }
91  }
92 
93  // Private stuff
94  QtObject {
95  id: d
96  property real startValue
97  property real minValue: {
98  if (direction == Direction.Horizontal) {
99  return startValue - maxTotalDragDistance;
100  } else if (Direction.isPositive(direction)) {
101  return startValue;
102  } else {
103  return startValue - maxTotalDragDistance;
104  }
105  }
106 
107  property real maxValue: Direction.isPositive(direction) ? startValue + maxTotalDragDistance
108  : startValue
109 
110  property var dragParent: dragArea.parent
111 
112  // The property of DragHandle's parent that will be modified
113  property string targetProp: {
114  if (stretch) {
115  Direction.isHorizontal(direction) ? "width" : "height";
116  } else {
117  Direction.isHorizontal(direction) ? "x" : "y";
118  }
119  }
120 
121  function limitMovement(inputStep) {
122  var targetValue = MathUtils.clamp(dragParent[targetProp] + inputStep, minValue, maxValue);
123  var step = targetValue - dragParent[targetProp];
124 
125  if (hintDisplacement == 0) {
126  return step;
127  }
128 
129  // we should not go behind hintingAnimation's current value
130  if (Direction.isPositive(direction)) {
131  if (dragParent[targetProp] + step < hintingAnimation.targetValue) {
132  step = hintingAnimation.targetValue - dragParent[targetProp];
133  }
134  } else {
135  if (dragParent[targetProp] + step > hintingAnimation.targetValue) {
136  step = hintingAnimation.targetValue - dragParent[targetProp];
137  }
138  }
139 
140  return step;
141  }
142 
143  function onFinishedRecognizedGesture() {
144  if (dragEvaluator.shouldAutoComplete()) {
145  completeDrag();
146  } else {
147  rollbackDrag();
148  }
149  }
150 
151  function completeDrag() {
152  if (dragParent.shown) {
153  dragParent.hide();
154  } else {
155  dragParent.show();
156  }
157  }
158 
159  function rollbackDrag() {
160  if (dragParent.shown) {
161  dragParent.show();
162  } else {
163  dragParent.hide();
164  }
165  }
166  }
167 
168  property alias edgeDragEvaluator: dragEvaluator
169 
170  EdgeDragEvaluator {
171  objectName: "edgeDragEvaluator"
172  id: dragEvaluator
173  // Effectively convert distance into the drag position projected onto the gesture direction axis
174  trackedPosition: Direction.isPositive(dragArea.direction) ? sceneDistance : -sceneDistance
175  maxDragDistance: maxTotalDragDistance
176  direction: dragArea.direction
177  }
178 
179  onDistanceChanged: {
180  if (dragging) {
181  // don't go the whole distance in order to smooth out the movement
182  var step = distance * 0.3;
183 
184  step = d.limitMovement(step);
185 
186  parent[d.targetProp] += step;
187  }
188  }
189 
190  onDraggingChanged: {
191  if (dragging) {
192  dragEvaluator.reset();
193  if (overrideStartValue !== undefined) {
194  d.startValue = overrideStartValue;
195  } else {
196  d.startValue = parent[d.targetProp];
197  }
198 
199  if (hintDisplacement > 0) {
200  hintingAnimation.targetValue = d.startValue;
201  hintingAnimation.start();
202  }
203  } else {
204  hintingAnimation.stop();
205  d.onFinishedRecognizedGesture();
206  }
207  }
208 }