on fe9d37e help sign in
unity8/qml/Stage/WindowResizeArea.qml
300 lines 11 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
22MouseArea {
23 id: root
25 anchors.margins: -borderThickness
27 hoverEnabled: target && !target.maximized // don't grab the resize under the panel
29 readonly property alias dragging: d.dragging
31 // The target item managed by this. Must be a parent or a sibling
32 // The area will anchor to it and manage resize events
33 property Item target: null
34 property int borderThickness: 0
35 property Item boundsItem
36 property int minWidth: 0
37 property int minHeight: 0
39 QtObject {
40 id: d
42 readonly property int maxSafeInt: 2147483647
43 readonly property int maxSizeIncrement: units.gu(40)
45 readonly property int minimumWidth: root.target ? Math.max(root.minWidth, root.target.minimumWidth) : root.minWidth
46 onMinimumWidthChanged: {
47 if (target.windowedWidth < minimumWidth) {
48 target.windowedWidth = minimumWidth;
49 }
50 }
51 readonly property int minimumHeight: root.target ? Math.max(root.minHeight, root.target.minimumHeight) : root.minHeight
52 onMinimumHeightChanged: {
53 if (target.windowedHeight < minimumHeight) {
54 target.windowedHeight = minimumHeight;
55 }
56 }
57 readonly property int maximumWidth: root.target && root.target.maximumWidth >= minimumWidth && root.target.maximumWidth > 0
58 ? root.target.maximumWidth : maxSafeInt
59 onMaximumWidthChanged: {
60 if (target.windowedWidth > maximumWidth) {
61 target.windowedWidth = maximumWidth;
62 }
63 }
64 readonly property int maximumHeight: root.target && root.target.maximumHeight >= minimumHeight && root.target.maximumHeight > 0
65 ? root.target.maximumHeight : maxSafeInt
66 onMaximumHeightChanged: {
67 if (target.windowedHeight > maximumHeight) {
68 target.windowedHeight = maximumHeight;
69 }
70 }
71 readonly property int widthIncrement: {
72 if (!root.target) {
73 return 1;
74 }
75 if (root.target.widthIncrement > 0) {
76 if (root.target.widthIncrement < maxSizeIncrement) {
77 return root.target.widthIncrement;
78 } else {
79 return maxSizeIncrement;
80 }
81 } else {
82 return 1;
83 }
84 }
85 readonly property int heightIncrement: {
86 if (!root.target) {
87 return 1;
88 }
89 if (root.target.heightIncrement > 0) {
90 if (root.target.heightIncrement < maxSizeIncrement) {
91 return root.target.heightIncrement;
92 } else {
93 return maxSizeIncrement;
94 }
95 } else {
96 return 1;
97 }
98 }
100 property bool leftBorder: false
101 property bool rightBorder: false
102 property bool topBorder: false
103 property bool bottomBorder: false
105 // true - A change in surface size will cause the left border of the window to move accordingly.
106 // The window's right border will stay in the same position.
107 // false - a change in surface size will cause the right border of the window to move accordingly.
108 // The window's left border will stay in the same position.
109 property bool moveLeftBorder: false
111 // true - A change in surface size will cause the top border of the window to move accordingly.
112 // The window's bottom border will stay in the same position.
113 // false - a change in surface size will cause the bottom border of the window to move accordingly.
114 // The window's top border will stay in the same position.
115 property bool moveTopBorder: false
117 property bool dragging: false
118 property real startMousePosX
119 property real startMousePosY
120 property real startX
121 property real startY
122 property real startWidth
123 property real startHeight
124 property real currentWidth
125 property real currentHeight
127 readonly property string cursorName: {
128 if (root.containsMouse || root.pressed) {
129 if (leftBorder && !topBorder && !bottomBorder) {
130 return "left_side";
131 } else if (rightBorder && !topBorder && !bottomBorder) {
132 return "right_side";
133 } else if (topBorder && !leftBorder && !rightBorder) {
134 return "top_side";
135 } else if (bottomBorder && !leftBorder && !rightBorder) {
136 return "bottom_side";
137 } else if (leftBorder && topBorder) {
138 return "top_left_corner";
139 } else if (leftBorder && bottomBorder) {
140 return "bottom_left_corner";
141 } else if (rightBorder && topBorder) {
142 return "top_right_corner";
143 } else if (rightBorder && bottomBorder) {
144 return "bottom_right_corner";
145 } else {
146 return "";
147 }
148 } else {
149 return "";
150 }
151 }
152 onCursorNameChanged: {
153 Mir.cursorName = cursorName;
154 }
155 Component.onDestruction: {
156 // TODO Qt 5.8 has fixed the problem with containsMouse
157 // not being updated when the MouseArea that had containsMouse
158 // is hidden/removed. When we start using Qt 5.8 we should
159 // try to fix this scenario
160 // two windows side by side
161 // cursor in the resize left area of the right one
162 // close window by Alt+F4
163 // cursor should change to resize right of the left one
164 // currently changes to ""
165 Mir.cursorName = "";
166 }
168 function updateBorders() {
169 leftBorder = mouseX <= borderThickness;
170 rightBorder = mouseX >= width - borderThickness;
171 topBorder = mouseY <= borderThickness;
172 bottomBorder = mouseY >= height - borderThickness;
173 }
174 }
176 Timer {
177 id: resetBordersToMoveTimer
178 interval: 2000
179 onTriggered: {
180 d.moveLeftBorder = false;
181 d.moveTopBorder = false;
182 }
183 }
185 onPressedChanged: {
186 if (pressed) {
187 d.updateBorders();
188 resetBordersToMoveTimer.stop();
189 d.moveLeftBorder = d.leftBorder;
190 d.moveTopBorder = d.topBorder;
192 var pos = mapToItem(root.target.parent, mouseX, mouseY);
193 d.startMousePosX = pos.x;
194 d.startMousePosY = pos.y;
195 d.startX = target.windowedX;
196 d.startY = target.windowedY;
197 d.startWidth = target.width;
198 d.startHeight = target.height;
199 d.currentWidth = target.width;
200 d.currentHeight = target.height;
201 d.dragging = true;
202 } else {
203 resetBordersToMoveTimer.start();
204 d.dragging = false;
205 if (containsMouse) {
206 d.updateBorders();
207 }
208 }
209 }
211 onEntered: {
212 if (!pressed) {
213 d.updateBorders();
214 }
215 }
217 onPositionChanged: {
218 if (!pressed) {
219 d.updateBorders();
220 }
222 if (!d.dragging) {
223 return;
224 }
226 var pos = mapToItem(target.parent, mouse.x, mouse.y);
228 var deltaX = Math.floor((pos.x - d.startMousePosX) / d.widthIncrement) * d.widthIncrement;
229 var deltaY = Math.floor((pos.y - d.startMousePosY) / d.heightIncrement) * d.heightIncrement;
231 if (d.leftBorder) {
232 var newTargetX = d.startX + deltaX;
233 var rightBorderX = target.windowedX + target.width;
234 if (rightBorderX > newTargetX + d.minimumWidth) {
235 if (rightBorderX < newTargetX + d.maximumWidth) {
236 target.windowedWidth = rightBorderX - newTargetX;
237 } else {
238 target.windowedWidth = d.maximumWidth;
239 }
240 } else {
241 target.windowedWidth = d.minimumWidth;
242 }
244 } else if (d.rightBorder) {
245 var newWidth = d.startWidth + deltaX;
246 if (newWidth > d.minimumWidth) {
247 if (newWidth < d.maximumWidth) {
248 target.windowedWidth = newWidth;
249 } else {
250 target.windowedWidth = d.maximumWidth;
251 }
252 } else {
253 target.windowedWidth = d.minimumWidth;
254 }
255 }
257 if (d.topBorder) {
258 var bounds = boundsItem.mapToItem(target.parent, 0, 0, boundsItem.width, boundsItem.height);
259 var newTargetY = Math.max(d.startY + deltaY, bounds.y);
260 var bottomBorderY = target.windowedY + target.height;
261 if (bottomBorderY > newTargetY + d.minimumHeight) {
262 if (bottomBorderY < newTargetY + d.maximumHeight) {
263 target.windowedHeight = bottomBorderY - newTargetY;
264 } else {
265 target.windowedHeight = d.maximumHeight;
266 }
267 } else {
268 target.windowedHeight = d.minimumHeight;
269 }
271 } else if (d.bottomBorder) {
272 var newHeight = d.startHeight + deltaY;
273 if (newHeight > d.minimumHeight) {
274 if (newHeight < d.maximumHeight) {
275 target.windowedHeight = newHeight;
276 } else {
277 target.windowedHeight = d.maximumHeight;
278 }
279 } else {
280 target.windowedHeight = d.minimumHeight;
281 }
282 }
283 }
285 Connections {
286 target: root.target
287 onWidthChanged: {
288 if (d.moveLeftBorder) {
289 target.windowedX += d.currentWidth - target.width;
290 }
291 d.currentWidth = target.width;
292 }
293 onHeightChanged: {
294 if (d.moveTopBorder) {
295 target.windowedY += d.currentHeight - target.height;
296 }
297 d.currentHeight = target.height;
298 }
299 }