on bde717c help sign in
unity8/qml/Stage/MoveHandler.qml
193 lines 9.8 kB history raw
1/*
2 * Copyright (C) 2014-2016 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.4
18import Unity.Application 0.1 // For Mir singleton
19import Ubuntu.Components 1.3
20import Utils 0.1
21import "../Components"
23QtObject {
24 id: root
26 property Item target // appDelegate
27 property real buttonsWidth: 0
28 property Item boundsItem
29 property real boundsTopMargin: 0
31 readonly property bool dragging: priv.dragging
33 signal fakeMaximizeAnimationRequested(real amount)
34 signal fakeMaximizeLeftAnimationRequested(real amount)
35 signal fakeMaximizeRightAnimationRequested(real amount)
36 signal fakeMaximizeTopLeftAnimationRequested(real amount)
37 signal fakeMaximizeTopRightAnimationRequested(real amount)
38 signal fakeMaximizeBottomLeftAnimationRequested(real amount)
39 signal fakeMaximizeBottomRightAnimationRequested(real amount)
40 signal stopFakeAnimation()
42 property QtObject priv: QtObject {
43 property real distanceX
44 property real distanceY
45 property bool dragging
47 readonly property int triggerArea: units.gu(8)
48 property bool nearLeftEdge: target.maximizedLeft
49 property bool nearTopEdge: target.maximized
50 property bool nearRightEdge: target.maximizedRight
51 property bool nearTopLeftCorner: target.maximizedTopLeft
52 property bool nearTopRightCorner: target.maximizedTopRight
53 property bool nearBottomLeftCorner: target.maximizedBottomLeft
54 property bool nearBottomRightCorner: target.maximizedBottomRight
56 property Timer mouseDownTimer: Timer {
57 interval: 175
58 onTriggered: Mir.cursorName = "grabbing"
59 }
61 function resetEdges() {
62 nearLeftEdge = false;
63 nearRightEdge = false;
64 nearTopEdge = false;
65 nearTopLeftCorner = false;
66 nearTopRightCorner = false;
67 nearBottomLeftCorner = false;
68 nearBottomRightCorner = false;
69 }
71 // return the progress of mouse pointer movement from 0 to 1 within a corner square of the screen
72 // 0 -> before the mouse enters the square
73 // 1 -> mouse is in the outer corner
74 // a is the corner, b is the mouse pos
75 function progressInCorner(ax, ay, bx, by) {
76 // distance of two points, a and b, in pixels
77 var distance = Math.sqrt(Math.pow(bx-ax, 2) + Math.pow(by-ay, 2));
78 // length of the triggerArea square diagonal
79 var diagLength = Math.sqrt(2 * priv.triggerArea * priv.triggerArea);
80 var ratio = 1 - (distance / diagLength);
82 // everything "outside" of our square from the center is 1
83 var mousePosBoundsCoords = target.mapToItem(root.boundsItem, bx, by);
84 return root.boundsItem.contains(mousePosBoundsCoords) ? ratio : 1;
85 }
86 property real progress: 0
87 }
89 function handlePressedChanged(pressed, pressedButtons, mouseX, mouseY) {
90 if (pressed && pressedButtons === Qt.LeftButton) {
91 var pos = mapToItem(target, mouseX, mouseY);
92 if (target.anyMaximized) {
93 // keep distanceX relative to the normal window width minus the window control buttons (+spacing)
94 // so that dragging it back doesn't make the window jump around to weird positions, away from the mouse pointer
95 priv.distanceX = MathUtils.clampAndProject(pos.x, 0, target.width, buttonsWidth, target.normalWidth);
96 priv.distanceY = MathUtils.clampAndProject(pos.y, 0, target.height, 0, target.normalHeight);
97 } else {
98 priv.distanceX = pos.x;
99 priv.distanceY = pos.y;
100 }
102 priv.dragging = true;
103 priv.mouseDownTimer.start();
104 } else {
105 priv.dragging = false;
106 priv.mouseDownTimer.stop();
107 Mir.cursorName = "";
108 }
109 }
111 function handlePositionChanged(mouse, sensingPoints) {
112 if (priv.dragging) {
113 priv.mouseDownTimer.stop();
114 Mir.cursorName = "grabbing";
116 // restore from maximized when dragging away from edges/corners; guard against inadvertent changes when going into maximized state
117 if (target.anyMaximized && !target.windowedTransitionRunning) {
118 priv.progress = 0;
119 target.requestRestore();
120 }
122 var pos = mapToItem(target.parent, mouse.x, mouse.y); // How can that work if we're just a QtObject (not an Item)?
123 var bounds = boundsItem.mapToItem(target.parent, 0, 0, boundsItem.width, boundsItem.height);
124 bounds.y += boundsTopMargin;
125 bounds.height -= boundsTopMargin;
126 // Use integer coordinate values to ensure that target is left in a pixel-aligned
127 // position. Mouse movement could have subpixel precision, yielding a fractional
128 // mouse position.
129 target.windowedX = Math.round(pos.x - priv.distanceX);
130 target.windowedY = Math.round(Math.max(pos.y - priv.distanceY, bounds.top));
132 if (sensingPoints) { // edge/corner detection when dragging via the touch overlay
133 if (sensingPoints.topLeft.x < priv.triggerArea && sensingPoints.topLeft.y < bounds.top + priv.triggerArea
134 && target.canBeCornerMaximized) { // top left
135 priv.progress = priv.progressInCorner(bounds.left, bounds.top, sensingPoints.topLeft.x, sensingPoints.topLeft.y);
136 priv.resetEdges();
137 priv.nearTopLeftCorner = true;
138 root.fakeMaximizeTopLeftAnimationRequested(priv.progress);
139 } else if (sensingPoints.topRight.x > bounds.right - priv.triggerArea && sensingPoints.topRight.y < bounds.top + priv.triggerArea
140 && target.canBeCornerMaximized) { // top right
141 priv.progress = priv.progressInCorner(bounds.right, bounds.top, sensingPoints.topRight.x, sensingPoints.topRight.y);
142 priv.resetEdges();
143 priv.nearTopRightCorner = true;
144 root.fakeMaximizeTopRightAnimationRequested(priv.progress);
145 } else if (sensingPoints.bottomLeft.x < priv.triggerArea && sensingPoints.bottomLeft.y > bounds.bottom - priv.triggerArea
146 && target.canBeCornerMaximized) { // bottom left
147 priv.progress = priv.progressInCorner(bounds.left, bounds.bottom, sensingPoints.bottomLeft.x, sensingPoints.bottomLeft.y);
148 priv.resetEdges();
149 priv.nearBottomLeftCorner = true;
150 root.fakeMaximizeBottomLeftAnimationRequested(priv.progress);
151 } else if (sensingPoints.bottomRight.x > bounds.right - priv.triggerArea && sensingPoints.bottomRight.y > bounds.bottom - priv.triggerArea
152 && target.canBeCornerMaximized) { // bottom right
153 priv.progress = priv.progressInCorner(bounds.right, bounds.bottom, sensingPoints.bottomRight.x, sensingPoints.bottomRight.y);
154 priv.resetEdges();
155 priv.nearBottomRightCorner = true;
156 root.fakeMaximizeBottomRightAnimationRequested(priv.progress);
157 } else if (sensingPoints.left.x < priv.triggerArea && target.canBeMaximizedLeftRight) { // left
158 priv.progress = MathUtils.clampAndProject(sensingPoints.left.x, priv.triggerArea, 0, 0, 1);
159 priv.resetEdges();
160 priv.nearLeftEdge = true;
161 root.fakeMaximizeLeftAnimationRequested(priv.progress);
162 } else if (sensingPoints.right.x > bounds.right - priv.triggerArea && target.canBeMaximizedLeftRight) { // right
163 priv.progress = MathUtils.clampAndProject(sensingPoints.right.x, bounds.right - priv.triggerArea, bounds.right, 0, 1);
164 priv.resetEdges();
165 priv.nearRightEdge = true;
166 root.fakeMaximizeRightAnimationRequested(priv.progress);
167 } else if (sensingPoints.top.y < bounds.top + priv.triggerArea && target.canBeMaximized) { // top
168 priv.progress = MathUtils.clampAndProject(sensingPoints.top.y, bounds.top + priv.triggerArea, 0, 0, 1);
169 priv.resetEdges();
170 priv.nearTopEdge = true;
171 root.fakeMaximizeAnimationRequested(priv.progress);
172 } else if (priv.nearLeftEdge || priv.nearRightEdge || priv.nearTopEdge || priv.nearTopLeftCorner || priv.nearTopRightCorner ||
173 priv.nearBottomLeftCorner || priv.nearBottomRightCorner) {
174 priv.progress = 0;
175 priv.resetEdges();
176 root.stopFakeAnimation();
177 }
178 }
179 }
180 }
182 function handleReleased(touchMode) {
183 if (touchMode) {
184 priv.progress = 0;
185 priv.resetEdges();
186 }
187 if ((target.state == "normal" || target.state == "restored") && priv.progress == 0) {
188 // save the x/y to restore to
189 target.restoredX = target.x;
190 target.restoredY = target.y;
191 }
192 }