on bedf8bc help sign in
unity8/qml/Stage/WindowResizeArea.qml
367 lines 14 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 Ubuntu.Components 1.3
19import Utils 0.1
20import Unity.Application 0.1 // for Mir.cursorName
21import "../Components/PanelState"
23MouseArea {
24 id: root
26 anchors.fill: target
27 anchors.margins: -borderThickness
29 hoverEnabled: target && !target.maximized // don't grab the resize under the panel
31 property var windowStateStorage: WindowStateStorage
32 readonly property alias dragging: d.dragging
34 // The target item managed by this. Must be a parent or a sibling
35 // The area will anchor to it and manage resize events
36 property Item target: null
37 property string windowId: ""
38 property int borderThickness: 0
39 property int minWidth: 0
40 property int minHeight: 0
41 property int defaultWidth: units.gu(60)
42 property int defaultHeight: units.gu(50)
43 property int screenWidth: 0
44 property int screenHeight: 0
45 property int leftMargin: 0
47 QtObject {
48 id: priv
49 objectName: "priv"
51 property int normalX: 0
52 property int normalY: 0
53 property int normalWidth: 0
54 property int normalHeight: 0
56 function updateNormalGeometry() {
57 if (root.target.state == "normal") {
58 normalX = root.target.windowedX
59 normalY = root.target.windowedY
60 normalWidth = root.target.width
61 normalHeight = root.target.height
62 }
63 }
64 }
66 Connections {
67 target: root.target
68 onXChanged: priv.updateNormalGeometry();
69 onYChanged: priv.updateNormalGeometry();
70 onWidthChanged: priv.updateNormalGeometry();
71 onHeightChanged: priv.updateNormalGeometry();
72 }
74 function loadWindowState() {
75 var windowGeometry = windowStateStorage.getGeometry(root.windowId,
76 Qt.rect(target.windowedX, target.windowedY, defaultWidth, defaultHeight));
79 print("loading Window state!", root.windowId, windowGeometry.width, windowGeometry.height)
80 target.windowedWidth = Qt.binding(function() { return Math.min(Math.max(windowGeometry.width, d.minimumWidth), screenWidth - root.leftMargin); });
81 target.windowedHeight = Qt.binding(function() { return Math.min(Math.max(windowGeometry.height, d.minimumHeight),
82 root.screenHeight - (target.fullscreen ? 0 : PanelState.panelHeight)); });
83 target.windowedX = Qt.binding(function() { return Math.max(Math.min(windowGeometry.x, root.screenWidth - root.leftMargin - target.windowedWidth),
84 (target.fullscreen ? 0 : root.leftMargin)); });
85 target.windowedY = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.windowedHeight), PanelState.panelHeight); });
87 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
88 switch (windowState) {
89 case WindowStateStorage.WindowStateNormal:
90 break;
91 case WindowStateStorage.WindowStateMaximized:
92 target.maximize(false);
93 break;
94 case WindowStateStorage.WindowStateMaximizedLeft:
95 target.maximizeLeft(false);
96 break;
97 case WindowStateStorage.WindowStateMaximizedRight:
98 target.maximizeRight(false);
99 break;
100 case WindowStateStorage.WindowStateMaximizedHorizontally:
101 target.maximizeHorizontally(false);
102 break;
103 case WindowStateStorage.WindowStateMaximizedVertically:
104 target.maximizeVertically(false);
105 break;
106 default:
107 console.warn("Unsupported window state");
108 break;
109 }
111 priv.updateNormalGeometry();
112 }
114 function saveWindowState() {
115 windowStateStorage.saveState(root.windowId, target.windowState & ~WindowStateStorage.WindowStateMinimized); // clear the minimized bit when saving
116 windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight));
117 }
119 QtObject {
120 id: d
122 readonly property int maxSafeInt: 2147483647
123 readonly property int maxSizeIncrement: units.gu(40)
125 readonly property int minimumWidth: root.target ? Math.max(root.minWidth, root.target.minimumWidth) : root.minWidth
126 onMinimumWidthChanged: {
127 if (target.windowedWidth < minimumWidth) {
128 target.windowedWidth = minimumWidth;
129 }
130 }
131 readonly property int minimumHeight: root.target ? Math.max(root.minHeight, root.target.minimumHeight) : root.minHeight
132 onMinimumHeightChanged: {
133 if (target.windowedHeight < minimumHeight) {
134 target.windowedHeight = minimumHeight;
135 }
136 }
137 readonly property int maximumWidth: root.target && root.target.maximumWidth >= minimumWidth && root.target.maximumWidth > 0
138 ? root.target.maximumWidth : maxSafeInt
139 onMaximumWidthChanged: {
140 if (target.windowedWidth > maximumWidth) {
141 target.windowedWidth = maximumWidth;
142 }
143 }
144 readonly property int maximumHeight: root.target && root.target.maximumHeight >= minimumHeight && root.target.maximumHeight > 0
145 ? root.target.maximumHeight : maxSafeInt
146 onMaximumHeightChanged: {
147 if (target.windowedHeight > maximumHeight) {
148 target.windowedHeight = maximumHeight;
149 }
150 }
151 readonly property int widthIncrement: {
152 if (!root.target) {
153 return 1;
154 }
155 if (root.target.widthIncrement > 0) {
156 if (root.target.widthIncrement < maxSizeIncrement) {
157 return root.target.widthIncrement;
158 } else {
159 return maxSizeIncrement;
160 }
161 } else {
162 return 1;
163 }
164 }
165 readonly property int heightIncrement: {
166 if (!root.target) {
167 return 1;
168 }
169 if (root.target.heightIncrement > 0) {
170 if (root.target.heightIncrement < maxSizeIncrement) {
171 return root.target.heightIncrement;
172 } else {
173 return maxSizeIncrement;
174 }
175 } else {
176 return 1;
177 }
178 }
180 property bool leftBorder: false
181 property bool rightBorder: false
182 property bool topBorder: false
183 property bool bottomBorder: false
185 // true - A change in surface size will cause the left border of the window to move accordingly.
186 // The window's right border will stay in the same position.
187 // false - a change in surface size will cause the right border of the window to move accordingly.
188 // The window's left border will stay in the same position.
189 property bool moveLeftBorder: false
191 // true - A change in surface size will cause the top border of the window to move accordingly.
192 // The window's bottom border will stay in the same position.
193 // false - a change in surface size will cause the bottom border of the window to move accordingly.
194 // The window's top border will stay in the same position.
195 property bool moveTopBorder: false
197 property bool dragging: false
198 property real startMousePosX
199 property real startMousePosY
200 property real startX
201 property real startY
202 property real startWidth
203 property real startHeight
204 property real currentWidth
205 property real currentHeight
207 readonly property string cursorName: {
208 if (root.containsMouse || root.pressed) {
209 if (leftBorder && !topBorder && !bottomBorder) {
210 return "left_side";
211 } else if (rightBorder && !topBorder && !bottomBorder) {
212 return "right_side";
213 } else if (topBorder && !leftBorder && !rightBorder) {
214 return "top_side";
215 } else if (bottomBorder && !leftBorder && !rightBorder) {
216 return "bottom_side";
217 } else if (leftBorder && topBorder) {
218 return "top_left_corner";
219 } else if (leftBorder && bottomBorder) {
220 return "bottom_left_corner";
221 } else if (rightBorder && topBorder) {
222 return "top_right_corner";
223 } else if (rightBorder && bottomBorder) {
224 return "bottom_right_corner";
225 } else {
226 return "";
227 }
228 } else {
229 return "";
230 }
231 }
232 onCursorNameChanged: {
233 Mir.cursorName = cursorName;
234 }
236 function updateBorders() {
237 leftBorder = mouseX <= borderThickness;
238 rightBorder = mouseX >= width - borderThickness;
239 topBorder = mouseY <= borderThickness;
240 bottomBorder = mouseY >= height - borderThickness;
241 }
242 }
244 Timer {
245 id: resetBordersToMoveTimer
246 interval: 2000
247 onTriggered: {
248 d.moveLeftBorder = false;
249 d.moveTopBorder = false;
250 }
251 }
253 onPressedChanged: {
254 if (pressed) {
255 d.updateBorders();
256 resetBordersToMoveTimer.stop();
257 d.moveLeftBorder = d.leftBorder;
258 d.moveTopBorder = d.topBorder;
260 var pos = mapToItem(root.target.parent, mouseX, mouseY);
261 d.startMousePosX = pos.x;
262 d.startMousePosY = pos.y;
263 d.startX = target.windowedX;
264 d.startY = target.windowedY;
265 d.startWidth = target.width;
266 d.startHeight = target.height;
267 d.currentWidth = target.width;
268 d.currentHeight = target.height;
269 d.dragging = true;
270 } else {
271 resetBordersToMoveTimer.start();
272 d.dragging = false;
273 if (containsMouse) {
274 d.updateBorders();
275 }
276 }
277 }
279 onEntered: {
280 if (!pressed) {
281 d.updateBorders();
282 }
283 }
285 onPositionChanged: {
286 if (!pressed) {
287 d.updateBorders();
288 }
290 if (!d.dragging) {
291 return;
292 }
294 var pos = mapToItem(target.parent, mouse.x, mouse.y);
296 var deltaX = Math.floor((pos.x - d.startMousePosX) / d.widthIncrement) * d.widthIncrement;
297 var deltaY = Math.floor((pos.y - d.startMousePosY) / d.heightIncrement) * d.heightIncrement;
299 if (d.leftBorder) {
300 var newTargetX = d.startX + deltaX;
301 var rightBorderX = target.windowedX + target.width;
302 if (rightBorderX > newTargetX + d.minimumWidth) {
303 if (rightBorderX < newTargetX + d.maximumWidth) {
304 target.windowedWidth = rightBorderX - newTargetX;
305 } else {
306 target.windowedWidth = d.maximumWidth;
307 }
308 } else {
309 target.windowedWidth = d.minimumWidth;
310 }
312 } else if (d.rightBorder) {
313 var newWidth = d.startWidth + deltaX;
314 if (newWidth > d.minimumWidth) {
315 if (newWidth < d.maximumWidth) {
316 target.windowedWidth = newWidth;
317 } else {
318 target.windowedWidth = d.maximumWidth;
319 }
320 } else {
321 target.windowedWidth = d.minimumWidth;
322 }
323 }
325 if (d.topBorder) {
326 var newTargetY = Math.max(d.startY + deltaY, PanelState.panelHeight); // disallow resizing up past Panel
327 var bottomBorderY = target.windowedY + target.height;
328 if (bottomBorderY > newTargetY + d.minimumHeight) {
329 if (bottomBorderY < newTargetY + d.maximumHeight) {
330 target.windowedHeight = bottomBorderY - newTargetY;
331 } else {
332 target.windowedHeight = d.maximumHeight;
333 }
334 } else {
335 target.windowedHeight = d.minimumHeight;
336 }
338 } else if (d.bottomBorder) {
339 var newHeight = d.startHeight + deltaY;
340 if (newHeight > d.minimumHeight) {
341 if (newHeight < d.maximumHeight) {
342 target.windowedHeight = newHeight;
343 } else {
344 target.windowedHeight = d.maximumHeight;
345 }
346 } else {
347 target.windowedHeight = d.minimumHeight;
348 }
349 }
350 }
352 Connections {
353 target: root.target
354 onWidthChanged: {
355 if (d.moveLeftBorder) {
356 target.windowedX += d.currentWidth - target.width;
357 }
358 d.currentWidth = target.width;
359 }
360 onHeightChanged: {
361 if (d.moveTopBorder) {
362 target.windowedY += d.currentHeight - target.height;
363 }
364 d.currentHeight = target.height;
365 }
366 }