2 * Copyright (C) 2014-2015 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 Component.onCompleted: {
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 if (windowState === WindowStateStorage.WindowStateMaximized) {
85 target.maximize(false)
87 priv.updateNormalGeometry();
90 Component.onDestruction: {
91 windowStateStorage.saveState(root.windowId, target.state == "maximized" ? WindowStateStorage.WindowStateMaximized : WindowStateStorage.WindowStateNormal)
92 windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight))
98 readonly property int maxSafeInt: 2147483647
99 readonly property int maxSizeIncrement: units.gu(40)
101 readonly property int minimumWidth: root.target ? Math.max(root.minWidth, root.target.minimumWidth) : root.minWidth
102 onMinimumWidthChanged: {
103 if (target.requestedWidth < minimumWidth) {
104 target.requestedWidth = minimumWidth;
107 readonly property int minimumHeight: root.target ? Math.max(root.minHeight, root.target.minimumHeight) : root.minHeight
108 onMinimumHeightChanged: {
109 if (target.requestedHeight < minimumHeight) {
110 target.requestedHeight = minimumHeight;
113 readonly property int maximumWidth: root.target && root.target.maximumWidth >= minimumWidth && root.target.maximumWidth > 0
114 ? root.target.maximumWidth : maxSafeInt
115 onMaximumWidthChanged: {
116 if (target.requestedWidth > maximumWidth) {
117 target.requestedWidth = maximumWidth;
120 readonly property int maximumHeight: root.target && root.target.maximumHeight >= minimumHeight && root.target.maximumHeight > 0
121 ? root.target.maximumHeight : maxSafeInt
122 onMaximumHeightChanged: {
123 if (target.requestedHeight > maximumHeight) {
124 target.requestedHeight = maximumHeight;
127 readonly property int widthIncrement: {
131 if (root.target.widthIncrement > 0) {
132 if (root.target.widthIncrement < maxSizeIncrement) {
133 return root.target.widthIncrement;
135 return maxSizeIncrement;
141 readonly property int heightIncrement: {
145 if (root.target.heightIncrement > 0) {
146 if (root.target.heightIncrement < maxSizeIncrement) {
147 return root.target.heightIncrement;
149 return maxSizeIncrement;
156 property bool leftBorder: false
157 property bool rightBorder: false
158 property bool topBorder: false
159 property bool bottomBorder: false
161 // true - A change in surface size will cause the left border of the window to move accordingly.
162 // The window's right border will stay in the same position.
163 // false - a change in surface size will cause the right border of the window to move accordingly.
164 // The window's left border will stay in the same position.
165 property bool moveLeftBorder: false
167 // true - A change in surface size will cause the top border of the window to move accordingly.
168 // The window's bottom border will stay in the same position.
169 // false - a change in surface size will cause the bottom border of the window to move accordingly.
170 // The window's top border will stay in the same position.
171 property bool moveTopBorder: false
173 property bool dragging: false
174 property real startMousePosX
175 property real startMousePosY
178 property real startWidth
179 property real startHeight
180 property real currentWidth
181 property real currentHeight
183 property string cursorName: {
184 if (root.containsMouse || root.pressed) {
185 if (leftBorder && !topBorder && !bottomBorder) {
187 } else if (rightBorder && !topBorder && !bottomBorder) {
189 } else if (topBorder && !leftBorder && !rightBorder) {
191 } else if (bottomBorder && !leftBorder && !rightBorder) {
192 return "bottom_side";
193 } else if (leftBorder && topBorder) {
194 return "top_left_corner";
195 } else if (leftBorder && bottomBorder) {
196 return "bottom_left_corner";
197 } else if (rightBorder && topBorder) {
198 return "top_right_corner";
199 } else if (rightBorder && bottomBorder) {
200 return "bottom_right_corner";
208 onCursorNameChanged: {
209 Mir.cursorName = cursorName;
212 function updateBorders() {
213 leftBorder = mouseX <= borderThickness;
214 rightBorder = mouseX >= width - borderThickness;
215 topBorder = mouseY <= borderThickness;
216 bottomBorder = mouseY >= height - borderThickness;
221 id: resetBordersToMoveTimer
224 d.moveLeftBorder = false;
225 d.moveTopBorder = false;
230 var pos = mapToItem(target.parent, mouseX, mouseY);
234 resetBordersToMoveTimer.stop();
235 d.moveLeftBorder = d.leftBorder;
236 d.moveTopBorder = d.topBorder;
238 var pos = mapToItem(root.target.parent, mouseX, mouseY);
239 d.startMousePosX = pos.x;
240 d.startMousePosY = pos.y;
243 d.startWidth = target.width;
244 d.startHeight = target.height;
245 d.currentWidth = target.width;
246 d.currentHeight = target.height;
249 resetBordersToMoveTimer.start();
272 var pos = mapToItem(target.parent, mouse.x, mouse.y);
274 var deltaX = Math.floor((pos.x - d.startMousePosX) / d.widthIncrement) * d.widthIncrement;
275 var deltaY = Math.floor((pos.y - d.startMousePosY) / d.heightIncrement) * d.heightIncrement;
278 var newTargetX = d.startX + deltaX;
279 var rightBorderX = target.x + target.width;
280 if (rightBorderX > newTargetX + d.minimumWidth) {
281 if (rightBorderX < newTargetX + d.maximumWidth) {
282 target.requestedWidth = rightBorderX - newTargetX;
284 target.requestedWidth = d.maximumWidth;
287 target.requestedWidth = d.minimumWidth;
290 } else if (d.rightBorder) {
291 var newWidth = d.startWidth + deltaX;
292 if (newWidth > d.minimumWidth) {
293 if (newWidth < d.maximumWidth) {
294 target.requestedWidth = newWidth;
296 target.requestedWidth = d.maximumWidth;
299 target.requestedWidth = d.minimumWidth;
304 var newTargetY = Math.max(d.startY + deltaY, PanelState.panelHeight); // disallow resizing up past Panel
305 var bottomBorderY = target.y + target.height;
306 if (bottomBorderY > newTargetY + d.minimumHeight) {
307 if (bottomBorderY < newTargetY + d.maximumHeight) {
308 target.requestedHeight = bottomBorderY - newTargetY;
310 target.requestedHeight = d.maximumHeight;
313 target.requestedHeight = d.minimumHeight;
316 } else if (d.bottomBorder) {
317 var newHeight = d.startHeight + deltaY;
318 if (newHeight > d.minimumHeight) {
319 if (newHeight < d.maximumHeight) {
320 target.requestedHeight = newHeight;
322 target.requestedHeight = d.maximumHeight;
325 target.requestedHeight = d.minimumHeight;
333 if (d.moveLeftBorder) {
334 target.x += d.currentWidth - target.width;
336 d.currentWidth = target.width;
339 if (d.moveTopBorder) {
340 target.y += d.currentHeight - target.height;
342 d.currentHeight = target.height;