Unity 8
WindowResizeArea.qml
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  */
16 
17 import QtQuick 2.4
18 import Ubuntu.Components 1.3
19 import Utils 0.1
20 import Unity.Application 0.1 // for Mir.cursorName
21 
22 MouseArea {
23  id: root
24  anchors.fill: target
25  anchors.margins: -borderThickness
26 
27  hoverEnabled: true
28 
29  property var windowStateStorage: WindowStateStorage
30 
31  // The target item managed by this. Must be a parent or a sibling
32  // The area will anchor to it and manage move and resize events
33  property Item target: null
34  property string windowId: ""
35  property int borderThickness: 0
36  property int minWidth: 0
37  property int minHeight: 0
38 
39  QtObject {
40  id: priv
41  objectName: "priv"
42 
43  property int normalX: 0
44  property int normalY: 0
45  property int normalWidth: 0
46  property int normalHeight: 0
47 
48  function updateNormalGeometry() {
49  if (root.target.state == "normal") {
50  normalX = root.target.x
51  normalY = root.target.y
52  normalWidth = root.target.width
53  normalHeight = root.target.height
54  }
55  }
56  }
57 
58  Connections {
59  target: root.target
60  onXChanged: priv.updateNormalGeometry();
61  onYChanged: priv.updateNormalGeometry();
62  onWidthChanged: priv.updateNormalGeometry();
63  onHeightChanged: priv.updateNormalGeometry();
64  }
65 
66  Component.onCompleted: {
67  var windowGeometry = windowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
68  if (windowGeometry !== undefined) {
69  target.x = windowGeometry.x
70  target.y = windowGeometry.y
71  target.width = windowGeometry.width
72  target.height = windowGeometry.height
73  }
74  var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
75  if (windowState === WindowStateStorage.WindowStateMaximized) {
76  target.maximize(false)
77  }
78  priv.updateNormalGeometry();
79  }
80 
81  Component.onDestruction: {
82  windowStateStorage.saveState(root.windowId, target.state == "maximized" ? WindowStateStorage.WindowStateMaximized : WindowStateStorage.WindowStateNormal)
83  windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight))
84  }
85 
86  QtObject {
87  id: d
88  property bool leftBorder: false
89  property bool rightBorder: false
90  property bool topBorder: false
91  property bool bottomBorder: false
92 
93  property bool dragging: false
94  property real startMousePosX
95  property real startMousePosY
96  property real startX
97  property real startY
98  property real startWidth
99  property real startHeight
100 
101  property string cursorName: {
102  if (root.containsMouse || root.pressed) {
103  if (leftBorder && !topBorder && !bottomBorder) {
104  return "left_side";
105  } else if (rightBorder && !topBorder && !bottomBorder) {
106  return "right_side";
107  } else if (topBorder && !leftBorder && !rightBorder) {
108  return "top_side";
109  } else if (bottomBorder && !leftBorder && !rightBorder) {
110  return "bottom_side";
111  } else if (leftBorder && topBorder) {
112  return "top_left_corner";
113  } else if (leftBorder && bottomBorder) {
114  return "bottom_left_corner";
115  } else if (rightBorder && topBorder) {
116  return "top_right_corner";
117  } else if (rightBorder && bottomBorder) {
118  return "bottom_right_corner";
119  } else {
120  return "";
121  }
122  } else {
123  return "";
124  }
125  }
126  onCursorNameChanged: {
127  Mir.cursorName = cursorName;
128  }
129 
130  function updateBorders() {
131  leftBorder = mouseX <= borderThickness;
132  rightBorder = mouseX >= width - borderThickness;
133  topBorder = mouseY <= borderThickness;
134  bottomBorder = mouseY >= height - borderThickness;
135  }
136  }
137 
138  onPressedChanged: {
139  var pos = mapToItem(target.parent, mouseX, mouseY);
140 
141  if (pressed) {
142  d.updateBorders();
143  var pos = mapToItem(root.target.parent, mouseX, mouseY);
144  d.startMousePosX = pos.x;
145  d.startMousePosY = pos.y;
146  d.startX = target.x;
147  d.startY = target.y;
148  d.startWidth = target.width;
149  d.startHeight = target.height;
150  d.dragging = true;
151  } else {
152  d.dragging = false;
153  if (containsMouse) {
154  d.updateBorders();
155  }
156  }
157  }
158 
159  onEntered: {
160  if (!pressed) {
161  d.updateBorders();
162  }
163  }
164 
165  onPositionChanged: {
166  if (!pressed) {
167  d.updateBorders();
168  }
169 
170  if (!d.dragging) {
171  return;
172  }
173 
174  var pos = mapToItem(target.parent, mouse.x, mouse.y);
175 
176  var deltaX = pos.x - d.startMousePosX;
177  var deltaY = pos.y - d.startMousePosY;
178 
179  if (d.leftBorder) {
180  var newTargetX = d.startX + deltaX;
181  if (target.x + target.width > newTargetX + minWidth) {
182  target.width = target.x + target.width - newTargetX;
183  target.x = newTargetX;
184  } else {
185  target.x = target.x + target.width - minWidth;
186  target.width = minWidth;
187  }
188 
189  } else if (d.rightBorder) {
190  if (d.startWidth + deltaX >= minWidth) {
191  target.width = d.startWidth + deltaX;
192  } else {
193  target.width = minWidth;
194  }
195  }
196 
197  if (d.topBorder) {
198  var newTargetY = d.startY + deltaY;
199  if (target.y + target.height > newTargetY + minHeight) {
200  target.height = target.y + target.height - newTargetY;
201  target.y = newTargetY;
202  } else {
203  target.y = target.y + target.height - minHeight;
204  target.height = minHeight;
205  }
206 
207  } else if (d.bottomBorder) {
208  if (d.startHeight + deltaY >= minHeight) {
209  target.height = d.startHeight + deltaY;
210  } else {
211  target.height = minHeight;
212  }
213  }
214  }
215 }