2 * Copyright (C) 2014-2016 Canonical, Ltd.
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.
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.
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/>.
18 import Ubuntu.Components 1.3
20 import Unity.Application 0.1 // for Mir.cursorName
21 import "../Components/PanelState"
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
43 property int leftMargin: 0
49 property int normalX: 0
50 property int normalY: 0
51 property int normalWidth: 0
52 property int normalHeight: 0
54 function updateNormalGeometry() {
55 if (root.target.state == "normal") {
56 normalX = root.target.x
57 normalY = root.target.y
58 normalWidth = root.target.width
59 normalHeight = root.target.height
66 onXChanged: priv.updateNormalGeometry();
67 onYChanged: priv.updateNormalGeometry();
68 onWidthChanged: priv.updateNormalGeometry();
69 onHeightChanged: priv.updateNormalGeometry();
72 function loadWindowState() {
73 var windowGeometry = windowStateStorage.getGeometry(root.windowId,
74 Qt.rect(target.x, target.y, defaultWidth, defaultHeight));
76 target.requestedWidth = Qt.binding(function() { return Math.min(Math.max(windowGeometry.width, d.minimumWidth), screenWidth - root.leftMargin); });
77 target.requestedHeight = Qt.binding(function() { return Math.min(Math.max(windowGeometry.height, d.minimumHeight),
78 root.screenHeight - (target.fullscreen ? 0 : PanelState.panelHeight)); });
79 target.x = Qt.binding(function() { return Math.max(Math.min(windowGeometry.x, root.screenWidth - root.leftMargin - target.requestedWidth),
80 (target.fullscreen ? 0 : root.leftMargin)); });
81 target.y = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight); });
83 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
84 switch (windowState) {
85 case WindowStateStorage.WindowStateNormal:
87 case WindowStateStorage.WindowStateMaximized:
88 target.maximize(false);
90 case WindowStateStorage.WindowStateMaximizedLeft:
91 target.maximizeLeft(false);
93 case WindowStateStorage.WindowStateMaximizedRight:
94 target.maximizeRight(false);
96 case WindowStateStorage.WindowStateMaximizedHorizontally:
97 target.maximizeHorizontally(false);
99 case WindowStateStorage.WindowStateMaximizedVertically:
100 target.maximizeVertically(false);
103 console.warn("Unsupported window state");
107 priv.updateNormalGeometry();
110 function saveWindowState() {
111 windowStateStorage.saveState(root.windowId, target.windowState & ~WindowStateStorage.WindowStateMinimized); // clear the minimized bit when saving
112 windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight));
118 readonly property int maxSafeInt: 2147483647
119 readonly property int maxSizeIncrement: units.gu(40)
121 readonly property int minimumWidth: root.target ? Math.max(root.minWidth, root.target.minimumWidth) : root.minWidth
122 onMinimumWidthChanged: {
123 if (target.requestedWidth < minimumWidth) {
124 target.requestedWidth = minimumWidth;
127 readonly property int minimumHeight: root.target ? Math.max(root.minHeight, root.target.minimumHeight) : root.minHeight
128 onMinimumHeightChanged: {
129 if (target.requestedHeight < minimumHeight) {
130 target.requestedHeight = minimumHeight;
133 readonly property int maximumWidth: root.target && root.target.maximumWidth >= minimumWidth && root.target.maximumWidth > 0
134 ? root.target.maximumWidth : maxSafeInt
135 onMaximumWidthChanged: {
136 if (target.requestedWidth > maximumWidth) {
137 target.requestedWidth = maximumWidth;
140 readonly property int maximumHeight: root.target && root.target.maximumHeight >= minimumHeight && root.target.maximumHeight > 0
141 ? root.target.maximumHeight : maxSafeInt
142 onMaximumHeightChanged: {
143 if (target.requestedHeight > maximumHeight) {
144 target.requestedHeight = maximumHeight;
147 readonly property int widthIncrement: {
151 if (root.target.widthIncrement > 0) {
152 if (root.target.widthIncrement < maxSizeIncrement) {
153 return root.target.widthIncrement;
155 return maxSizeIncrement;
161 readonly property int heightIncrement: {
165 if (root.target.heightIncrement > 0) {
166 if (root.target.heightIncrement < maxSizeIncrement) {
167 return root.target.heightIncrement;
169 return maxSizeIncrement;
176 property bool leftBorder: false
177 property bool rightBorder: false
178 property bool topBorder: false
179 property bool bottomBorder: false
181 // true - A change in surface size will cause the left border of the window to move accordingly.
182 // The window's right border will stay in the same position.
183 // false - a change in surface size will cause the right border of the window to move accordingly.
184 // The window's left border will stay in the same position.
185 property bool moveLeftBorder: false
187 // true - A change in surface size will cause the top border of the window to move accordingly.
188 // The window's bottom border will stay in the same position.
189 // false - a change in surface size will cause the bottom border of the window to move accordingly.
190 // The window's top border will stay in the same position.
191 property bool moveTopBorder: false
193 property bool dragging: false
194 property real startMousePosX
195 property real startMousePosY
198 property real startWidth
199 property real startHeight
200 property real currentWidth
201 property real currentHeight
203 property string cursorName: {
204 if (root.containsMouse || root.pressed) {
205 if (leftBorder && !topBorder && !bottomBorder) {
207 } else if (rightBorder && !topBorder && !bottomBorder) {
209 } else if (topBorder && !leftBorder && !rightBorder) {
211 } else if (bottomBorder && !leftBorder && !rightBorder) {
212 return "bottom_side";
213 } else if (leftBorder && topBorder) {
214 return "top_left_corner";
215 } else if (leftBorder && bottomBorder) {
216 return "bottom_left_corner";
217 } else if (rightBorder && topBorder) {
218 return "top_right_corner";
219 } else if (rightBorder && bottomBorder) {
220 return "bottom_right_corner";
228 onCursorNameChanged: {
229 Mir.cursorName = cursorName;
232 function updateBorders() {
233 leftBorder = mouseX <= borderThickness;
234 rightBorder = mouseX >= width - borderThickness;
235 topBorder = mouseY <= borderThickness;
236 bottomBorder = mouseY >= height - borderThickness;
241 id: resetBordersToMoveTimer
244 d.moveLeftBorder = false;
245 d.moveTopBorder = false;
250 var pos = mapToItem(target.parent, mouseX, mouseY);
254 resetBordersToMoveTimer.stop();
255 d.moveLeftBorder = d.leftBorder;
256 d.moveTopBorder = d.topBorder;
258 var pos = mapToItem(root.target.parent, mouseX, mouseY);
259 d.startMousePosX = pos.x;
260 d.startMousePosY = pos.y;
263 d.startWidth = target.width;
264 d.startHeight = target.height;
265 d.currentWidth = target.width;
266 d.currentHeight = target.height;
269 resetBordersToMoveTimer.start();
292 var pos = mapToItem(target.parent, mouse.x, mouse.y);
294 var deltaX = Math.floor((pos.x - d.startMousePosX) / d.widthIncrement) * d.widthIncrement;
295 var deltaY = Math.floor((pos.y - d.startMousePosY) / d.heightIncrement) * d.heightIncrement;
298 var newTargetX = d.startX + deltaX;
299 var rightBorderX = target.x + target.width;
300 if (rightBorderX > newTargetX + d.minimumWidth) {
301 if (rightBorderX < newTargetX + d.maximumWidth) {
302 target.requestedWidth = rightBorderX - newTargetX;
304 target.requestedWidth = d.maximumWidth;
307 target.requestedWidth = d.minimumWidth;
310 } else if (d.rightBorder) {
311 var newWidth = d.startWidth + deltaX;
312 if (newWidth > d.minimumWidth) {
313 if (newWidth < d.maximumWidth) {
314 target.requestedWidth = newWidth;
316 target.requestedWidth = d.maximumWidth;
319 target.requestedWidth = d.minimumWidth;
324 var newTargetY = Math.max(d.startY + deltaY, PanelState.panelHeight); // disallow resizing up past Panel
325 var bottomBorderY = target.y + target.height;
326 if (bottomBorderY > newTargetY + d.minimumHeight) {
327 if (bottomBorderY < newTargetY + d.maximumHeight) {
328 target.requestedHeight = bottomBorderY - newTargetY;
330 target.requestedHeight = d.maximumHeight;
333 target.requestedHeight = d.minimumHeight;
336 } else if (d.bottomBorder) {
337 var newHeight = d.startHeight + deltaY;
338 if (newHeight > d.minimumHeight) {
339 if (newHeight < d.maximumHeight) {
340 target.requestedHeight = newHeight;
342 target.requestedHeight = d.maximumHeight;
345 target.requestedHeight = d.minimumHeight;
353 if (d.moveLeftBorder) {
354 target.x += d.currentWidth - target.width;
356 d.currentWidth = target.width;
359 if (d.moveTopBorder) {
360 target.y += d.currentHeight - target.height;
362 d.currentHeight = target.height;