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  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  }
84 
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  }
96 
97  // Private stuff
98  QtObject {
99  id: d
100  property var previousStatus: DirectionalDragArea.WaitingForTouch
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
106 
107  property var dragParent: dragArea.parent
108 
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  }
117 
118  function limitMovement(inputStep) {
119  var targetValue = MathUtils.clamp(dragParent[targetProp] + inputStep, minValue, maxValue);
120  var step = targetValue - dragParent[targetProp];
121 
122  if (hintDisplacement == 0) {
123  return step;
124  }
125 
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  }
136 
137  return step;
138  }
139 
140  function onFinishedRecognizedGesture() {
141  if (dragEvaluator.shouldAutoComplete()) {
142  completeDrag();
143  } else {
144  rollbackDrag();
145  }
146  }
147 
148  function completeDrag() {
149  if (dragParent.shown) {
150  dragParent.hide();
151  } else {
152  dragParent.show();
153  }
154  }
155 
156  function rollbackDrag() {
157  if (dragParent.shown) {
158  dragParent.show();
159  } else {
160  dragParent.hide();
161  }
162  }
163  }
164 
165  property alias edgeDragEvaluator: dragEvaluator
166 
167  EdgeDragEvaluator {
168  objectName: "edgeDragEvaluator"
169  id: dragEvaluator
170  // Effectively convert distance into the drag position projected onto the gesture direction axis
171  trackedPosition: Direction.isPositive(dragArea.direction) ? sceneDistance : -sceneDistance
172  maxDragDistance: maxTotalDragDistance
173  direction: dragArea.direction
174  }
175 
176  onDistanceChanged: {
177  if (status === DirectionalDragArea.Recognized) {
178  // don't go the whole distance in order to smooth out the movement
179  var step = distance * 0.3;
180 
181  step = d.limitMovement(step);
182 
183  parent[d.targetProp] += step;
184  }
185  }
186 
187  onStatusChanged: {
188  if (status === DirectionalDragArea.WaitingForTouch) {
189  hintingAnimation.stop();
190  if (d.previousStatus === DirectionalDragArea.Recognized) {
191  d.onFinishedRecognizedGesture();
192  } else /* d.previousStatus === DirectionalDragArea.Undecided */ {
193  // Gesture was rejected.
194  d.rollbackDrag();
195  }
196  } else /* Undecided || Recognized */ {
197  if (d.previousStatus === DirectionalDragArea.WaitingForTouch) {
198  dragEvaluator.reset();
199  d.startValue = parent[d.targetProp];
200 
201  if (hintDisplacement > 0) {
202  hintingAnimation.targetValue = d.startValue;
203  hintingAnimation.start();
204  }
205  }
206  }
207 
208  d.previousStatus = status;
209  }
210 }