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"
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
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.requestedX
59 normalY = root.target.requestedY
60 normalWidth = root.target.width
61 normalHeight = root.target.height
68 onXChanged: priv.updateNormalGeometry();
69 onYChanged: priv.updateNormalGeometry();
70 onWidthChanged: priv.updateNormalGeometry();
71 onHeightChanged: priv.updateNormalGeometry();
74 function loadWindowState() {
75 var windowGeometry = windowStateStorage.getGeometry(root.windowId,
76 Qt.rect(target.requestedX, target.requestedY, defaultWidth, defaultHeight));
78 target.requestedWidth = Qt.binding(function() { return Math.min(Math.max(windowGeometry.width, d.minimumWidth), screenWidth - root.leftMargin); });
79 target.requestedHeight = Qt.binding(function() { return Math.min(Math.max(windowGeometry.height, d.minimumHeight),
80 root.screenHeight - (target.fullscreen ? 0 : PanelState.panelHeight)); });
81 target.requestedX = Qt.binding(function() { return Math.max(Math.min(windowGeometry.x, root.screenWidth - root.leftMargin - target.requestedWidth),
82 (target.fullscreen ? 0 : root.leftMargin)); });
83 target.requestedY = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight); });
85 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
86 switch (windowState) {
87 case WindowStateStorage.WindowStateNormal:
89 case WindowStateStorage.WindowStateMaximized:
90 target.maximize(false);
92 case WindowStateStorage.WindowStateMaximizedLeft:
93 target.maximizeLeft(false);
95 case WindowStateStorage.WindowStateMaximizedRight:
96 target.maximizeRight(false);
98 case WindowStateStorage.WindowStateMaximizedHorizontally:
99 target.maximizeHorizontally(false);
101 case WindowStateStorage.WindowStateMaximizedVertically:
102 target.maximizeVertically(false);
105 console.warn("Unsupported window state");
109 priv.updateNormalGeometry();
112 function saveWindowState() {
113 windowStateStorage.saveState(root.windowId, target.windowState & ~WindowStateStorage.WindowStateMinimized); // clear the minimized bit when saving
114 windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight));
120 readonly property int maxSafeInt: 2147483647
121 readonly property int maxSizeIncrement: units.gu(40)
123 readonly property int minimumWidth: root.target ? Math.max(root.minWidth, root.target.minimumWidth) : root.minWidth
124 onMinimumWidthChanged: {
125 if (target.requestedWidth < minimumWidth) {
126 target.requestedWidth = minimumWidth;
129 readonly property int minimumHeight: root.target ? Math.max(root.minHeight, root.target.minimumHeight) : root.minHeight
130 onMinimumHeightChanged: {
131 if (target.requestedHeight < minimumHeight) {
132 target.requestedHeight = minimumHeight;
135 readonly property int maximumWidth: root.target && root.target.maximumWidth >= minimumWidth && root.target.maximumWidth > 0
136 ? root.target.maximumWidth : maxSafeInt
137 onMaximumWidthChanged: {
138 if (target.requestedWidth > maximumWidth) {
139 target.requestedWidth = maximumWidth;
142 readonly property int maximumHeight: root.target && root.target.maximumHeight >= minimumHeight && root.target.maximumHeight > 0
143 ? root.target.maximumHeight : maxSafeInt
144 onMaximumHeightChanged: {
145 if (target.requestedHeight > maximumHeight) {
146 target.requestedHeight = maximumHeight;
149 readonly property int widthIncrement: {
153 if (root.target.widthIncrement > 0) {
154 if (root.target.widthIncrement < maxSizeIncrement) {
155 return root.target.widthIncrement;
157 return maxSizeIncrement;
163 readonly property int heightIncrement: {
167 if (root.target.heightIncrement > 0) {
168 if (root.target.heightIncrement < maxSizeIncrement) {
169 return root.target.heightIncrement;
171 return maxSizeIncrement;
178 property bool leftBorder: false
179 property bool rightBorder: false
180 property bool topBorder: false
181 property bool bottomBorder: false
183 // true - A change in surface size will cause the left border of the window to move accordingly.
184 // The window's right border will stay in the same position.
185 // false - a change in surface size will cause the right border of the window to move accordingly.
186 // The window's left border will stay in the same position.
187 property bool moveLeftBorder: false
189 // true - A change in surface size will cause the top border of the window to move accordingly.
190 // The window's bottom border will stay in the same position.
191 // false - a change in surface size will cause the bottom border of the window to move accordingly.
192 // The window's top border will stay in the same position.
193 property bool moveTopBorder: false
195 property bool dragging: false
196 property real startMousePosX
197 property real startMousePosY
200 property real startWidth
201 property real startHeight
202 property real currentWidth
203 property real currentHeight
205 readonly property string cursorName: {
206 if (root.containsMouse || root.pressed) {
207 if (leftBorder && !topBorder && !bottomBorder) {
209 } else if (rightBorder && !topBorder && !bottomBorder) {
211 } else if (topBorder && !leftBorder && !rightBorder) {
213 } else if (bottomBorder && !leftBorder && !rightBorder) {
214 return "bottom_side";
215 } else if (leftBorder && topBorder) {
216 return "top_left_corner";
217 } else if (leftBorder && bottomBorder) {
218 return "bottom_left_corner";
219 } else if (rightBorder && topBorder) {
220 return "top_right_corner";
221 } else if (rightBorder && bottomBorder) {
222 return "bottom_right_corner";
230 onCursorNameChanged: {
231 Mir.cursorName = cursorName;
234 function updateBorders() {
235 leftBorder = mouseX <= borderThickness;
236 rightBorder = mouseX >= width - borderThickness;
237 topBorder = mouseY <= borderThickness;
238 bottomBorder = mouseY >= height - borderThickness;
243 id: resetBordersToMoveTimer
246 d.moveLeftBorder = false;
247 d.moveTopBorder = false;
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;
261 d.startX = target.requestedX;
262 d.startY = target.requestedY;
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.requestedX + 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.requestedY + 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.requestedX += d.currentWidth - target.width;
356 d.currentWidth = target.width;
359 if (d.moveTopBorder) {
360 target.requestedY += d.currentHeight - target.height;
362 d.currentHeight = target.height;