on bbbdccd help sign in
unity8/qml/Stages/WindowResizeArea.qml
342 lines 13 kB history raw
1/*
2 * Copyright (C) 2014-2015 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
25 anchors.fill: target
26 anchors.margins: -borderThickness
28 hoverEnabled: target && !target.maximized // don't grab the resize under the panel
30 property var windowStateStorage: WindowStateStorage
32 // The target item managed by this. Must be a parent or a sibling
33 // The area will anchor to it and manage move and resize events
34 property Item target: null
35 property string windowId: ""
36 property int borderThickness: 0
37 property int minWidth: 0
38 property int minHeight: 0
39 property int defaultWidth: units.gu(60)
40 property int defaultHeight: units.gu(50)
41 property int screenWidth: 0
42 property int screenHeight: 0
44 QtObject {
45 id: priv
46 objectName: "priv"
48 property int normalX: 0
49 property int normalY: 0
50 property int normalWidth: 0
51 property int normalHeight: 0
53 function updateNormalGeometry() {
54 if (root.target.state == "normal") {
55 normalX = root.target.x
56 normalY = root.target.y
57 normalWidth = root.target.width
58 normalHeight = root.target.height
59 }
60 }
61 }
63 Connections {
64 target: root.target
65 onXChanged: priv.updateNormalGeometry();
66 onYChanged: priv.updateNormalGeometry();
67 onWidthChanged: priv.updateNormalGeometry();
68 onHeightChanged: priv.updateNormalGeometry();
69 }
71 Component.onCompleted: {
72 var windowGeometry = windowStateStorage.getGeometry(root.windowId,
73 Qt.rect(target.x, target.y, defaultWidth, defaultHeight));
75 target.requestedWidth = Math.min(Math.max(windowGeometry.width, d.minimumWidth), screenWidth);
76 target.requestedHeight = Math.min(Math.max(windowGeometry.height, d.minimumHeight), root.screenHeight - PanelState.panelHeight);
77 target.x = Math.max(Math.min(windowGeometry.x, root.screenWidth - target.requestedWidth), 0)
78 target.y = Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight)
80 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
81 if (windowState === WindowStateStorage.WindowStateMaximized) {
82 target.maximize(false)
83 }
84 priv.updateNormalGeometry();
85 }
87 Component.onDestruction: {
88 windowStateStorage.saveState(root.windowId, target.state == "maximized" ? WindowStateStorage.WindowStateMaximized : WindowStateStorage.WindowStateNormal)
89 windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight))
90 }
92 QtObject {
93 id: d
95 readonly property int maxSafeInt: 2147483647
96 readonly property int maxSizeIncrement: units.gu(40)
98 readonly property int minimumWidth: root.target ? Math.max(root.minWidth, root.target.minimumWidth) : root.minWidth
99 onMinimumWidthChanged: {
100 if (target.requestedWidth < minimumWidth) {
101 target.requestedWidth = minimumWidth;
102 }
103 }
104 readonly property int minimumHeight: root.target ? Math.max(root.minHeight, root.target.minimumHeight) : root.minHeight
105 onMinimumHeightChanged: {
106 if (target.requestedHeight < minimumHeight) {
107 target.requestedHeight = minimumHeight;
108 }
109 }
110 readonly property int maximumWidth: root.target && root.target.maximumWidth >= minimumWidth && root.target.maximumWidth > 0
111 ? root.target.maximumWidth : maxSafeInt
112 onMaximumWidthChanged: {
113 if (target.requestedWidth > maximumWidth) {
114 target.requestedWidth = maximumWidth;
115 }
116 }
117 readonly property int maximumHeight: root.target && root.target.maximumHeight >= minimumHeight && root.target.maximumHeight > 0
118 ? root.target.maximumHeight : maxSafeInt
119 onMaximumHeightChanged: {
120 if (target.requestedHeight > maximumHeight) {
121 target.requestedHeight = maximumHeight;
122 }
123 }
124 readonly property int widthIncrement: {
125 if (!root.target) {
126 return 1;
127 }
128 if (root.target.widthIncrement > 0) {
129 if (root.target.widthIncrement < maxSizeIncrement) {
130 return root.target.widthIncrement;
131 } else {
132 return maxSizeIncrement;
133 }
134 } else {
135 return 1;
136 }
137 }
138 readonly property int heightIncrement: {
139 if (!root.target) {
140 return 1;
141 }
142 if (root.target.heightIncrement > 0) {
143 if (root.target.heightIncrement < maxSizeIncrement) {
144 return root.target.heightIncrement;
145 } else {
146 return maxSizeIncrement;
147 }
148 } else {
149 return 1;
150 }
151 }
153 property bool leftBorder: false
154 property bool rightBorder: false
155 property bool topBorder: false
156 property bool bottomBorder: false
158 // true - A change in surface size will cause the left border of the window to move accordingly.
159 // The window's right border will stay in the same position.
160 // false - a change in surface size will cause the right border of the window to move accordingly.
161 // The window's left border will stay in the same position.
162 property bool moveLeftBorder: false
164 // true - A change in surface size will cause the top border of the window to move accordingly.
165 // The window's bottom border will stay in the same position.
166 // false - a change in surface size will cause the bottom border of the window to move accordingly.
167 // The window's top border will stay in the same position.
168 property bool moveTopBorder: false
170 property bool dragging: false
171 property real startMousePosX
172 property real startMousePosY
173 property real startX
174 property real startY
175 property real startWidth
176 property real startHeight
177 property real currentWidth
178 property real currentHeight
180 property string cursorName: {
181 if (root.containsMouse || root.pressed) {
182 if (leftBorder && !topBorder && !bottomBorder) {
183 return "left_side";
184 } else if (rightBorder && !topBorder && !bottomBorder) {
185 return "right_side";
186 } else if (topBorder && !leftBorder && !rightBorder) {
187 return "top_side";
188 } else if (bottomBorder && !leftBorder && !rightBorder) {
189 return "bottom_side";
190 } else if (leftBorder && topBorder) {
191 return "top_left_corner";
192 } else if (leftBorder && bottomBorder) {
193 return "bottom_left_corner";
194 } else if (rightBorder && topBorder) {
195 return "top_right_corner";
196 } else if (rightBorder && bottomBorder) {
197 return "bottom_right_corner";
198 } else {
199 return "";
200 }
201 } else {
202 return "";
203 }
204 }
205 onCursorNameChanged: {
206 Mir.cursorName = cursorName;
207 }
209 function updateBorders() {
210 leftBorder = mouseX <= borderThickness;
211 rightBorder = mouseX >= width - borderThickness;
212 topBorder = mouseY <= borderThickness;
213 bottomBorder = mouseY >= height - borderThickness;
214 }
215 }
217 Timer {
218 id: resetBordersToMoveTimer
219 interval: 2000
220 onTriggered: {
221 d.moveLeftBorder = false;
222 d.moveTopBorder = false;
223 }
224 }
226 onPressedChanged: {
227 var pos = mapToItem(target.parent, mouseX, mouseY);
229 if (pressed) {
230 d.updateBorders();
231 resetBordersToMoveTimer.stop();
232 d.moveLeftBorder = d.leftBorder;
233 d.moveTopBorder = d.topBorder;
235 var pos = mapToItem(root.target.parent, mouseX, mouseY);
236 d.startMousePosX = pos.x;
237 d.startMousePosY = pos.y;
238 d.startX = target.x;
239 d.startY = target.y;
240 d.startWidth = target.width;
241 d.startHeight = target.height;
242 d.currentWidth = target.width;
243 d.currentHeight = target.height;
244 d.dragging = true;
245 } else {
246 resetBordersToMoveTimer.start();
247 d.dragging = false;
248 if (containsMouse) {
249 d.updateBorders();
250 }
251 }
252 }
254 onEntered: {
255 if (!pressed) {
256 d.updateBorders();
257 }
258 }
260 onPositionChanged: {
261 if (!pressed) {
262 d.updateBorders();
263 }
265 if (!d.dragging) {
266 return;
267 }
269 var pos = mapToItem(target.parent, mouse.x, mouse.y);
271 var deltaX = Math.floor((pos.x - d.startMousePosX) / d.widthIncrement) * d.widthIncrement;
272 var deltaY = Math.floor((pos.y - d.startMousePosY) / d.heightIncrement) * d.heightIncrement;
274 if (d.leftBorder) {
275 var newTargetX = d.startX + deltaX;
276 var rightBorderX = target.x + target.width;
277 if (rightBorderX > newTargetX + d.minimumWidth) {
278 if (rightBorderX < newTargetX + d.maximumWidth) {
279 target.requestedWidth = rightBorderX - newTargetX;
280 } else {
281 target.requestedWidth = d.maximumWidth;
282 }
283 } else {
284 target.requestedWidth = d.minimumWidth;
285 }
287 } else if (d.rightBorder) {
288 var newWidth = d.startWidth + deltaX;
289 if (newWidth > d.minimumWidth) {
290 if (newWidth < d.maximumWidth) {
291 target.requestedWidth = newWidth;
292 } else {
293 target.requestedWidth = d.maximumWidth;
294 }
295 } else {
296 target.requestedWidth = d.minimumWidth;
297 }
298 }
300 if (d.topBorder) {
301 var newTargetY = d.startY + deltaY;
302 var bottomBorderY = target.y + target.height;
303 if (bottomBorderY > newTargetY + d.minimumHeight) {
304 if (bottomBorderY < newTargetY + d.maximumHeight) {
305 target.requestedHeight = bottomBorderY - newTargetY;
306 } else {
307 target.requestedHeight = d.maximumHeight;
308 }
309 } else {
310 target.requestedHeight = d.minimumHeight;
311 }
313 } else if (d.bottomBorder) {
314 var newHeight = d.startHeight + deltaY;
315 if (newHeight > d.minimumHeight) {
316 if (newHeight < d.maximumHeight) {
317 target.requestedHeight = newHeight;
318 } else {
319 target.requestedHeight = d.maximumHeight;
320 }
321 } else {
322 target.requestedHeight = d.minimumHeight;
323 }
324 }
325 }
327 Connections {
328 target: root.target
329 onWidthChanged: {
330 if (d.moveLeftBorder) {
331 target.x += d.currentWidth - target.width;
332 }
333 d.currentWidth = target.width;
334 }
335 onHeightChanged: {
336 if (d.moveTopBorder) {
337 target.y += d.currentHeight - target.height;
338 }
339 d.currentHeight = target.height;
340 }
341 }