on xenial help sign in
add a WindowMoveResizeArea to properly move and resize windows
194546bMichael Zanetti
qml/Stages/WindowMoveResizeArea.qmlnew file+80
@@ -0,0 +1,80 @@
1+import QtQuick 2.3
2+import Ubuntu.Components 1.1
3+
4+MouseArea {
5+ id: root
6+ anchors.fill: target
7+ anchors.margins: -resizeHandleWidth
8+
9+ // The target item managed by this. Must be a parent or a sibling
10+ // The area will anchor to it and manage move and resize events
11+ property var target: null
12+ property int resizeHandleWidth: 0
13+ property int minWidth: 0
14+ property int minHeight: 0
15+
16+ QtObject {
17+ id: priv
18+ readonly property int windowWidth: root.width - root.resizeHandleWidth * 2
19+ readonly property int windowHeight: root.height - resizeHandleWidth * 2
20+
21+ property var startPoint
22+ property int startWidth
23+ property int startHeight
24+
25+ property bool resizeTop: false
26+ property bool resizeBottom: false
27+ property bool resizeLeft: false
28+ property bool resizeRight: false
29+
30+ }
31+
32+ onPressed: {
33+ priv.startPoint = mapToItem(null, Qt.point(mouse.x, mouse.y)).x;
34+ priv.startWidth = root.width;
35+ priv.startHeight = root.height;
36+ priv.resizeTop = mouseY < root.resizeHandleWidth;
37+ priv.resizeBottom = mouseY > (root.height - root.resizeHandleWidth);
38+ priv.resizeLeft = mouseX < root.resizeHandleWidth;
39+ priv.resizeRight = mouseX > (root.width - root.resizeHandleWidth);
40+ }
41+
42+ onPositionChanged: {
43+ var currentPoint = mapToItem(null, Qt.point(mouse.x, mouse.y)).x;
44+ var mouseDiff = Qt.point(currentPoint.x - priv.startPoint.x, currentPoint.y - priv.startPoint.y);
45+ var moveDiff = Qt.point(0, 0);
46+ var sizeDiff = Qt.point(0, 0);
47+ if (priv.resizeTop || priv.resizeBottom || priv.resizeLeft || priv.resizeRight) {
48+ if (priv.resizeTop) {
49+ moveDiff.y += mouseDiff.y;
50+ sizeDiff.y += -mouseDiff.y;
51+ } else if (priv.resizeBottom) {
52+ sizeDiff.y += mouse.y - priv.startPoint.y;
53+ priv.startPoint.y = mouse.y
54+ }
55+ if (priv.resizeLeft) {
56+ moveDiff.x += mouseDiff.x;
57+ sizeDiff.x += -mouseDiff.x;
58+ } else if (priv.resizeRight) {
59+ sizeDiff.x += mouse.x - priv.startPoint.x;
60+ priv.startPoint.x = mouse.x
61+ }
62+ if (priv.windowWidth + sizeDiff.x < root.minWidth) {
63+ sizeDiff.x = root.minWidth - priv.windowWidth;
64+ moveDiff.x = moveDiff.x == 0 ? 0 : -sizeDiff.x;
65+ }
66+ if (priv.windowHeight + sizeDiff.y < root.minHeight) {
67+ sizeDiff.y = root.minHeight - priv.windowHeight;
68+ moveDiff.y = moveDiff.y == 0 ? 0 : -sizeDiff.y;
69+ }
70+
71+ target.x += moveDiff.x;
72+ target.y += moveDiff.y;
73+ target.width += sizeDiff.x;
74+ target.height += sizeDiff.y;
75+ } else {
76+ target.x += mouseDiff.x;
77+ target.y += mouseDiff.y;
78+ }
79+ }
80+}
tests/qmltests/CMakeLists.txt+1−1
@@ -86,6 +86,6 @@ add_qml_test(Stages PhoneStage ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/
86add_qml_test(Stages SpreadDelegate ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
86add_qml_test(Stages SpreadDelegate ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
87add_qml_test(Stages SurfaceContainer ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
87add_qml_test(Stages SurfaceContainer ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
88add_qml_test(Stages SessionContainer ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
88add_qml_test(Stages SessionContainer ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
89−add_qml_test(Stages WindowResizer)
89+add_qml_test(Stages WindowMoveResizeArea)
90add_qml_test(Stages Splash)
90add_qml_test(Stages Splash)
91add_qml_test(Wizard Wizard)
91add_qml_test(Wizard Wizard)
tests/qmltests/Stages/tst_WindowMoveResizeArea.qmlnew file+138
@@ -0,0 +1,138 @@
1+/*
2+ * Copyright 2014 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.1
18+import QtQuick.Layouts 1.1
19+import QtTest 1.0
20+import Unity.Test 0.1 as UT
21+import ".."
22+import "../../../qml/Stages"
23+import Ubuntu.Components 0.1
24+import Ubuntu.Components.ListItems 1.0 as ListItem
25+import Unity.Application 0.1
26+
27+Item {
28+ id: root
29+ height: units.gu(60)
30+ width: units.gu(60)
31+
32+ Rectangle {
33+ id: fakeWindow
34+ height: units.gu(20)
35+ width: units.gu(20)
36+ color: "khaki"
37+ WindowMoveResizeArea {
38+ id: moveResizeArea
39+ target: fakeWindow
40+ resizeHandleWidth: units.gu(0.5)
41+ minWidth: units.gu(15)
42+ minHeight: units.gu(10)
43+ }
44+ }
45+
46+ UT.UnityTestCase {
47+ when: windowShown
48+
49+ function init() {
50+ fakeWindow.x = units.gu(20)
51+ fakeWindow.y = units.gu(20)
52+ fakeWindow.width = units.gu(20)
53+ fakeWindow.height = units.gu(20)
54+ }
55+
56+ function test_dragWindow_data() {
57+ return [
58+ { tag: "up", dx: 0, dy: units.gu(-10) },
59+ { tag: "down", dx: 0, dy: units.gu(10) },
60+ { tag: "left", dx: units.gu(-10), dy: 0 },
61+ { tag: "right", dx: units.gu(10), dy: 0 },
62+ { tag: "right/down", dx: units.gu(10), dy: units.gu(10) },
63+ { tag: "left/down", dx: units.gu(-10), dy: units.gu(10) }
64+ ]
65+ }
66+
67+ function test_dragWindow(data) {
68+ var initialWindowX = fakeWindow.x;
69+ var initialWindowY = fakeWindow.y;
70+ var initialWindowWidth = fakeWindow.width
71+ var initialWindowHeight = fakeWindow.height
72+
73+ var startDragX = initialWindowX + fakeWindow.width / 2;
74+ var startDragY = initialWindowY + fakeWindow.height / 2;
75+ mouseFlick(root, startDragX, startDragY, startDragX + data.dx, startDragY + data.dy, true, true, units.gu(.5), 10)
76+
77+ tryCompare(fakeWindow, "x", initialWindowX + data.dx)
78+ tryCompare(fakeWindow, "y", initialWindowX + data.dy)
79+
80+ compare(fakeWindow.height, initialWindowHeight);
81+ compare(fakeWindow.width, initialWindowWidth);
82+ }
83+
84+ function test_resizeWindowRightBottom_data() {
85+ return [
86+ { tag: "width", dx: units.gu(10), dy: 0 },
87+ { tag: "height", dx: 0, dy: units.gu(10) },
88+ { tag: "both", dx: units.gu(10), dy: units.gu(10) },
89+ { tag: "smaller than minimum size", dx: -units.gu(15), dy: -units.gu(15) }
90+ ]
91+ }
92+
93+ function test_resizeWindowRightBottom(data) {
94+ var initialWindowX = fakeWindow.x;
95+ var initialWindowY = fakeWindow.y;
96+ var initialWindowWidth = fakeWindow.width
97+ var initialWindowHeight = fakeWindow.height
98+
99+ var startDragX = initialWindowX + initialWindowWidth + 1
100+ var startDragY = initialWindowY + initialWindowHeight + 1
101+ mouseFlick(root, startDragX, startDragY, startDragX + data.dx, startDragY + data.dy, true, true, units.gu(.5), 10);
102+
103+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + data.dx, moveResizeArea.minWidth));
104+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + data.dy, moveResizeArea.minHeight));
105+
106+ compare(fakeWindow.x, initialWindowX);
107+ compare(fakeWindow.y, initialWindowY);
108+ }
109+
110+ function test_resizeWindowLeftTop_data() {
111+ return [
112+ { tag: "width", dx: -units.gu(10), dy: 0 },
113+ { tag: "height", dx: 0, dy: -units.gu(10) },
114+ { tag: "both", dx: -units.gu(10), dy: -units.gu(10) },
115+ { tag: "smaller than minimum size", dx: units.gu(15), dy: units.gu(15) }
116+ ]
117+ }
118+
119+ function test_resizeWindowLeftTop(data) {
120+ var initialWindowX = fakeWindow.x;
121+ var initialWindowY = fakeWindow.y;
122+ var initialWindowWidth = fakeWindow.width
123+ var initialWindowHeight = fakeWindow.height
124+
125+ var startDragX = initialWindowX - 1
126+ var startDragY = initialWindowY - 1
127+ mouseFlick(root, startDragX, startDragY, startDragX + data.dx, startDragY + data.dy, true, true, units.gu(.5), 10);
128+
129+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth - data.dx, moveResizeArea.minWidth));
130+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight - data.dy, moveResizeArea.minHeight));
131+
132+ var maxMoveX = initialWindowWidth - moveResizeArea.minWidth;
133+ var maxMoveY = initialWindowHeight - moveResizeArea.minHeight;
134+ compare(fakeWindow.x, Math.min(initialWindowX + data.dx, initialWindowX + maxMoveX));
135+ compare(fakeWindow.y, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));
136+ }
137+ }
138+}