on 5b03213 help sign in
unity8/Components/DragHandle.qml
211 lines 6.5 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
20import "Math.js" as MathLocal
22/*
23 Put a DragHandle inside a Showable to enable the user to drag it from that handle.
24 Main use case is to drag fullscreen Showables into the screen or off the screen.
26 This example shows a DragHandle placed on the right corner of a Showable, used
27 to slide it away, off the screen.
29 Showable {
30 x: 0
31 y: 0
32 width: ... // screen width
33 height: ... // screen height
34 shown: true
35 ...
36 DragHandle {
37 anchors.right: parent.right
38 anchors.top: parent.top
39 anchors.bottom: parent.bottom
40 width: units.gu(2)
42 direction: DirectionalDragArea::Leftwards
43 }
44 }
46 */
47EdgeDragArea {
48 id: dragArea
49 objectName: "dragHandle"
51 // Disable gesture detection by default when hinting is used.
52 // It doesn't make sense to have both.
53 distanceThreshold: hintDisplacement > 0 ? 0 : units.gu(1.5)
55 property bool stretch: false
57 property alias autoCompleteDragThreshold: dragEvaluator.dragThreshold
59 // How far you can drag
60 property real maxTotalDragDistance: {
61 if (stretch) {
62 0; // not enough context information to set a sensible default
63 } else {
64 Direction.isHorizontal(direction) ? parent.width : parent.height;
65 }
66 }
68 property real hintDisplacement: 0
69 SmoothedAnimation {
70 id: hintingAnimation
71 target: hintingAnimation
72 property: "targetValue"
73 duration: 150
74 velocity: -1
75 to: Direction.isPositive(direction) ? d.startValue + hintDisplacement
76 : d.startValue - hintDisplacement
77 property real targetValue
78 onTargetValueChanged: {
79 if (!running) {
80 return;
81 }
83 if (Direction.isPositive(direction)) {
84 if (parent[d.targetProp] < targetValue) {
85 parent[d.targetProp] = targetValue;
86 }
87 } else {
88 if (parent[d.targetProp] > targetValue) {
89 parent[d.targetProp] = targetValue;
90 }
91 }
92 }
93 }
95 // Private stuff
96 QtObject {
97 id: d
98 property var previousStatus: undefined
99 property real startValue
100 property real minValue: Direction.isPositive(direction) ? startValue
101 : startValue - maxTotalDragDistance
102 property real maxValue: Direction.isPositive(direction) ? startValue + maxTotalDragDistance
103 : startValue
105 property var dragParent: dragArea.parent
107 // The property of DragHandle's parent that will be modified
108 property string targetProp: {
109 if (stretch) {
110 Direction.isHorizontal(direction) ? "width" : "height";
111 } else {
112 Direction.isHorizontal(direction) ? "x" : "y";
113 }
114 }
116 function limitMovement(step) {
117 var targetValue = MathLocal.clamp(dragParent[targetProp] + step, minValue, maxValue);
118 var step = targetValue - dragParent[targetProp];
120 if (hintDisplacement == 0) {
121 return step;
122 }
124 // we should not go behind hintingAnimation's current value
125 if (Direction.isPositive(direction)) {
126 if (dragParent[targetProp] + step < hintingAnimation.targetValue) {
127 step = hintingAnimation.targetValue - dragParent[targetProp];
128 }
129 } else {
130 if (dragParent[targetProp] + step > hintingAnimation.targetValue) {
131 step = hintingAnimation.targetValue - dragParent[targetProp];
132 }
133 }
135 return step;
136 }
138 function onFinishedRecognizedGesture() {
139 if (dragEvaluator.shouldAutoComplete()) {
140 completeDrag();
141 } else {
142 rollbackDrag();
143 }
144 }
146 function completeDrag() {
147 if (dragParent.shown) {
148 dragParent.hide();
149 } else {
150 dragParent.show();
151 }
152 }
154 function rollbackDrag() {
155 if (dragParent.shown) {
156 dragParent.show();
157 } else {
158 dragParent.hide();
159 }
160 }
161 }
163 property alias edgeDragEvaluator: dragEvaluator
165 EdgeDragEvaluator {
166 objectName: "edgeDragEvaluator"
167 id: dragEvaluator
168 trackedPosition: sceneDistance
169 maxDragDistance: maxTotalDragDistance
170 direction: dragArea.direction
171 }
173 onDistanceChanged: {
174 if (status === DirectionalDragArea.Recognized) {
175 // don't go the whole distance in order to smooth out the movement
176 var step = distance * 0.3;
178 step = d.limitMovement(step);
180 parent[d.targetProp] += step;
181 }
182 }
184 onDraggingChanged: {
185 if (dragging) {
186 if (hintDisplacement > 0) {
187 hintingAnimation.targetValue = d.startValue;
188 hintingAnimation.start();
189 }
190 } else {
191 hintingAnimation.stop();
192 }
193 }
195 onStatusChanged: {
196 if (status === DirectionalDragArea.WaitingForTouch) {
197 if (d.previousStatus === DirectionalDragArea.Recognized) {
198 d.onFinishedRecognizedGesture();
199 }
200 d.startValue = parent[d.targetProp];
201 }
203 if (d.previousStatus === DirectionalDragArea.WaitingForTouch ||
204 d.previousStatus === undefined) {
205 dragEvaluator.reset();
206 d.startValue = parent[d.targetProp];
207 }
209 d.previousStatus = status;
210 }