on xenial help sign in
Take save/restore functions out of WindowResizeArea

They've no relationship with resizing whatsoever. Approved by: Lukáš Tinkl, Unity8 CI Bot

commitb5167ff52a730c43cb037d09b446488028ff8a4e authorDaniel d'Andrada <daniel.dandrada@canonical.com> dateWed 7 Dec 2016, 13:46 (10 years ago) parenteffea560c3b219
7 files changed+182−154
qml/Stage/MoveHandler.qml+2−2 qml/Stage/Stage.qml+39−9 qml/Stage/WindowResizeArea.qml+2−70 qml/Stage/WindowStateSaver.qml+63 tests/qmltests/Stage/tst_DesktopStage.qml+71 tests/qmltests/Stage/tst_WindowResizeArea.qml+1−66 tests/qmltests/tst_Shell.qml+4−7
qml/Stage/MoveHandler.qml+2−2
@@ -90,8 +90,8 @@ QtObject {
9090 if (target.anyMaximized) {
9191 // keep distanceX relative to the normal window width minus the window control buttons (+spacing)
9292 // so that dragging it back doesn't make the window jump around to weird positions, away from the mouse pointer
93− priv.distanceX = MathUtils.clampAndProject(pos.x, 0, target.width, buttonsWidth, target.resizeArea.normalWidth);
94− priv.distanceY = MathUtils.clampAndProject(pos.y, 0, target.height, 0, target.resizeArea.normalHeight);
93+ priv.distanceX = MathUtils.clampAndProject(pos.x, 0, target.width, buttonsWidth, target.normalWidth);
94+ priv.distanceY = MathUtils.clampAndProject(pos.y, 0, target.height, 0, target.normalHeight);
9595 } else {
9696 priv.distanceX = pos.x;
9797 priv.distanceY = pos.y;
qml/Stage/Stage.qml+39−9
@@ -710,6 +710,29 @@ FocusScope {
710710 property real restoredX
711711 property real restoredY
712712
713+ // Keeps track of the window geometry while in normal or restored state
714+ // Useful when returning from some maxmized state or when saving the geometry while maximized
715+ // FIXME: find a better solution
716+ property real normalX: 0
717+ property real normalY: 0
718+ property real normalWidth: 0
719+ property real normalHeight: 0
720+ function updateNormalGeometry() {
721+ if (appDelegate.state == "normal" || appDelegate.state == "restored") {
722+ normalX = appDelegate.requestedX;
723+ normalY = appDelegate.requestedY;
724+ normalWidth = appDelegate.width;
725+ normalHeight = appDelegate.height;
726+ }
727+ }
728+ Connections {
729+ target: appDelegate
730+ onXChanged: appDelegate.updateNormalGeometry();
731+ onYChanged: appDelegate.updateNormalGeometry();
732+ onWidthChanged: appDelegate.updateNormalGeometry();
733+ onHeightChanged: appDelegate.updateNormalGeometry();
734+ }
735+
713736 Binding {
714737 target: appDelegate
715738 property: "y"
@@ -850,6 +873,16 @@ FocusScope {
850873 MirFocusController.focusedSurface = null;
851874 }
852875 }
876+
877+ WindowStateSaver {
878+ id: windowStateSaver
879+ target: appDelegate
880+ screenWidth: appContainer.width
881+ screenHeight: appContainer.height
882+ leftMargin: root.leftMargin
883+ minimumY: PanelState.panelHeight
884+ }
885+
853886 Component.onCompleted: {
854887 if (application && application.rotatesWindowContents) {
855888 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
@@ -861,7 +894,7 @@ FocusScope {
861894 windowedX = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedX + units.gu(3) : (normalZ - 1) * units.gu(3)
862895 windowedY = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedY + units.gu(3) : normalZ * units.gu(3)
863896 // Now load any saved state. This needs to happen *after* the cascading!
864− resizeArea.loadWindowState();
897+ windowStateSaver.load();
865898
866899 // NB: We're differentiating if this delegate was created in response to a new entry in the model
867900 // or if the Repeater is just populating itself with delegates to match the model it received.
@@ -878,6 +911,8 @@ FocusScope {
878911 _constructing = false;
879912 }
880913 Component.onDestruction: {
914+ windowStateSaver.save();
915+
881916 if (!root.parent) {
882917 // This stage is about to be destroyed. Don't mess up with the model at this point
883918 return;
@@ -1480,27 +1515,22 @@ FocusScope {
14801515 id: resizeArea
14811516 objectName: "windowResizeArea"
14821517
1518+ anchors.fill: appDelegate
1519+
14831520 // workaround so that it chooses the correct resize borders when you drag from a corner ResizeGrip
14841521 anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
14851522
14861523 target: appDelegate
1524+ minimumY: PanelState.panelHeight // disallow resizing up past Panel
14871525 minWidth: units.gu(10)
14881526 minHeight: units.gu(10)
14891527 borderThickness: units.gu(2)
1490− windowId: model.application.appId // FIXME: Change this to point to windowId once we have such a thing
1491− screenWidth: appContainer.width
1492− screenHeight: appContainer.height
1493− leftMargin: root.leftMargin
14941528 enabled: false
14951529 visible: enabled
14961530
14971531 onPressed: {
14981532 appDelegate.focus = true;
14991533 }
1500−
1501− Component.onDestruction: {
1502− saveWindowState();
1503− }
15041534 }
15051535
15061536 DecoratedWindow {
qml/Stage/WindowResizeArea.qml+2−70
@@ -18,91 +18,23 @@ import QtQuick 2.4
1818import Ubuntu.Components 1.3
1919import Utils 0.1
2020import Unity.Application 0.1 // for Mir.cursorName
21−import "../Components/PanelState"
2221
2322MouseArea {
2423 id: root
2524
26− anchors.fill: target
2725 anchors.margins: -borderThickness
2826
2927 hoverEnabled: target && !target.maximized // don't grab the resize under the panel
3028
31− property var windowStateStorage: WindowStateStorage
3229 readonly property alias dragging: d.dragging
33− readonly property alias normalWidth: priv.normalWidth
34− readonly property alias normalHeight: priv.normalHeight
3530
3631 // The target item managed by this. Must be a parent or a sibling
3732 // The area will anchor to it and manage resize events
3833 property Item target: null
39− property string windowId: ""
4034 property int borderThickness: 0
35+ property real minimumY: -100000000 // By default, impose no limit
4136 property int minWidth: 0
4237 property int minHeight: 0
43− property int defaultWidth: units.gu(60)
44− property int defaultHeight: units.gu(50)
45− property int screenWidth: 0
46− property int screenHeight: 0
47− property int leftMargin: 0
48−
49− QtObject {
50− id: priv
51− objectName: "priv"
52−
53− property int normalX: 0
54− property int normalY: 0
55− property int normalWidth: 0
56− property int normalHeight: 0
57−
58− function updateNormalGeometry() {
59− if (root.target.state == "normal" || root.target.state == "restored") {
60− normalX = root.target.requestedX
61− normalY = root.target.requestedY
62− normalWidth = root.target.width
63− normalHeight = root.target.height
64− }
65− }
66− }
67−
68− Connections {
69− target: root.target
70− onXChanged: priv.updateNormalGeometry();
71− onYChanged: priv.updateNormalGeometry();
72− onWidthChanged: priv.updateNormalGeometry();
73− onHeightChanged: priv.updateNormalGeometry();
74− }
75−
76− function loadWindowState() {
77− var windowGeometry = windowStateStorage.getGeometry(root.windowId,
78− Qt.rect(target.windowedX, target.windowedY, defaultWidth, defaultHeight));
79−
80−
81− target.windowedWidth = Qt.binding(function() { return Math.min(Math.max(windowGeometry.width, d.minimumWidth), screenWidth - root.leftMargin); });
82− target.windowedHeight = Qt.binding(function() { return Math.min(Math.max(windowGeometry.height, d.minimumHeight),
83− root.screenHeight - (target.fullscreen ? 0 : PanelState.panelHeight)); });
84− target.windowedX = Qt.binding(function() { return Math.max(Math.min(windowGeometry.x, root.screenWidth - root.leftMargin - target.windowedWidth),
85− (target.fullscreen ? 0 : root.leftMargin)); });
86− target.windowedY = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.windowedHeight), PanelState.panelHeight); });
87−
88− var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
89− target.restore(false /* animated */, windowState);
90−
91− priv.updateNormalGeometry();
92−
93− // initialize the x/y to restore to
94− target.restoredX = priv.normalX;
95− target.restoredY = priv.normalY;
96− }
97−
98− function saveWindowState() {
99− var state = target.windowState;
100− if (state === WindowStateStorage.WindowStateRestored) {
101− state = WindowStateStorage.WindowStateNormal;
102− }
103− windowStateStorage.saveState(root.windowId, state & ~WindowStateStorage.WindowStateMinimized); // clear the minimized bit when saving
104− windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight));
105− }
10638
10739 QtObject {
10840 id: d
@@ -311,7 +243,7 @@ MouseArea {
311243 }
312244
313245 if (d.topBorder) {
314− var newTargetY = Math.max(d.startY + deltaY, PanelState.panelHeight); // disallow resizing up past Panel
246+ var newTargetY = Math.max(d.startY + deltaY, root.minimumY);
315247 var bottomBorderY = target.windowedY + target.height;
316248 if (bottomBorderY > newTargetY + d.minimumHeight) {
317249 if (bottomBorderY < newTargetY + d.maximumHeight) {
qml/Stage/WindowStateSaver.qmlnew file+63
@@ -0,0 +1,63 @@
1+/*
2+ * Copyright (C) 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+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import Utils 0.1
20+
21+QtObject {
22+ id: root
23+
24+ // set from outside
25+ property var target
26+ property int screenWidth: 0
27+ property int screenHeight: 0
28+ property int leftMargin: 0
29+ property int minimumY: 0
30+
31+ function load() {
32+ var defaultWidth = units.gu(60);
33+ var defaultHeight = units.gu(50);
34+ var windowGeometry = WindowStateStorage.getGeometry(target.appId,
35+ Qt.rect(target.windowedX, target.windowedY, defaultWidth, defaultHeight));
36+
37+ target.windowedWidth = Qt.binding(function() { return Math.min(Math.max(windowGeometry.width, target.minimumWidth), screenWidth - root.leftMargin); });
38+ target.windowedHeight = Qt.binding(function() { return Math.min(Math.max(windowGeometry.height, target.minimumHeight),
39+ screenHeight - (target.fullscreen ? 0 : minimumY)); });
40+ target.windowedX = Qt.binding(function() { return Math.max(Math.min(windowGeometry.x, screenWidth - root.leftMargin - target.windowedWidth),
41+ (target.fullscreen ? 0 : root.leftMargin)); });
42+ target.windowedY = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, screenHeight - target.windowedHeight), minimumY); });
43+
44+ var windowState = WindowStateStorage.getState(target.appId, WindowStateStorage.WindowStateNormal)
45+ target.restore(false /* animated */, windowState);
46+
47+ target.updateNormalGeometry();
48+
49+ // initialize the x/y to restore to
50+ target.restoredX = target.normalX;
51+ target.restoredY = target.normalY;
52+ }
53+
54+ function save() {
55+ var state = target.windowState;
56+ if (state === WindowStateStorage.WindowStateRestored) {
57+ state = WindowStateStorage.WindowStateNormal;
58+ }
59+
60+ WindowStateStorage.saveState(target.appId, state & ~WindowStateStorage.WindowStateMinimized); // clear the minimized bit when saving
61+ WindowStateStorage.saveGeometry(target.appId, Qt.rect(target.normalX, target.normalY, target.normalWidth, target.normalHeight));
62+ }
63+}
tests/qmltests/Stage/tst_DesktopStage.qml+71
@@ -790,6 +790,77 @@ Item {
790790 tryCompare(dialerAppDelegate, "state", "maximized");
791791 }
792792
793+ function test_saveRestoreSize() {
794+ var originalWindowCount = topSurfaceList.count;
795+ var appDelegate = startApplication("dialer-app");
796+ compare(topSurfaceList.count, originalWindowCount + 1);
797+
798+ var initialWindowX = appDelegate.windowedX;
799+ var initialWindowY = appDelegate.windowedY;
800+ var initialWindowWidth = appDelegate.width
801+ var initialWindowHeight = appDelegate.height
802+
803+ var resizeDelta = units.gu(5)
804+ var startDragX = initialWindowX + initialWindowWidth + 1
805+ var startDragY = initialWindowY + initialWindowHeight + 1
806+ mouseFlick(root, startDragX, startDragY, startDragX + resizeDelta, startDragY + resizeDelta, true, true, units.gu(.5), 10);
807+
808+ tryCompare(appDelegate, "width", initialWindowWidth + resizeDelta);
809+ tryCompare(appDelegate, "height", initialWindowHeight + resizeDelta);
810+
811+ // Close the window and restart the application
812+ var closeButton = findChild(appDelegate, "closeWindowButton");
813+ appDelegate = null;
814+ verify(closeButton);
815+ mouseClick(closeButton);
816+ tryCompare(topSurfaceList, "count", originalWindowCount);
817+ wait(100); // plus some spare room
818+ appDelegate = startApplication("dialer-app");
819+
820+ // Make sure its size is again the same as before
821+ tryCompare(appDelegate, "width", initialWindowWidth + resizeDelta);
822+ tryCompare(appDelegate, "height", initialWindowHeight + resizeDelta);
823+ }
824+
825+ function test_saveRestoreMaximized() {
826+ var originalWindowCount = topSurfaceList.count;
827+ var appDelegate = startApplication("dialer-app");
828+ compare(topSurfaceList.count, originalWindowCount + 1);
829+
830+ var initialWindowX = appDelegate.windowedX;
831+ var initialWindowY = appDelegate.windowedY;
832+
833+ var moveDelta = units.gu(5);
834+
835+ appDelegate.windowedX = initialWindowX + moveDelta
836+ appDelegate.windowedY = initialWindowY + moveDelta
837+
838+ // Now change the state to maximized. The window should not keep updating the stored values
839+ maximizeAppDelegate(appDelegate);
840+
841+ // Close the window and restart the application
842+ var closeButton = findChild(appDelegate, "closeWindowButton");
843+ appDelegate = null;
844+ verify(closeButton);
845+ mouseClick(closeButton);
846+ closeButton = null;
847+ tryCompare(topSurfaceList, "count", originalWindowCount);
848+ wait(100); // plus some spare room
849+ appDelegate = startApplication("dialer-app");
850+
851+ // Make sure it's again where we left it in normal state before destroying
852+ tryCompare(appDelegate, "windowedX", initialWindowX + moveDelta)
853+ tryCompare(appDelegate, "windowedY", initialWindowY + moveDelta)
854+
855+ // Make sure maximize() has been called after restoring
856+ tryCompare(appDelegate, "state", "maximized")
857+
858+ // clean up
859+ // click on restore button (same one as maximize)
860+ var maximizeButton = findChild(appDelegate, "maximizeWindowButton");
861+ mouseClick(maximizeButton);
862+ }
863+
793864 function test_grabbingCursorOnDecorationPress() {
794865 var appDelegate = startApplication("dialer-app");
795866 verify(appDelegate);
tests/qmltests/Stage/tst_WindowResizeArea.qml+1−66
@@ -88,22 +88,11 @@ Item {
8888
8989 WindowResizeArea {
9090 id: windowResizeArea
91+ anchors.fill: parent
9192 target: fakeWindow
9293 borderThickness: units.gu(2)
9394 minWidth: units.gu(15)
9495 minHeight: units.gu(10)
95− defaultWidth: units.gu(20)
96− defaultHeight: units.gu(20)
97− windowId: "test-window-id"
98− screenWidth: root.width
99− screenHeight: root.height
100−
101− Component.onCompleted: {
102− loadWindowState();
103− }
104− Component.onDestruction: {
105− saveWindowState();
106− }
10796 }
10897
10998 Rectangle {
@@ -312,30 +301,6 @@ Item {
312301 compare(fakeWindow.requestedY, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));
313302 }
314303
315− function test_saveRestoreSize() {
316− var initialWindowX = fakeWindow.windowedX;
317− var initialWindowY = fakeWindow.windowedY;
318− var initialWindowWidth = fakeWindow.width
319− var initialWindowHeight = fakeWindow.height
320−
321− var resizeDelta = units.gu(5)
322− var startDragX = initialWindowX + initialWindowWidth + 1
323− var startDragY = initialWindowY + initialWindowHeight + 1
324− mouseFlick(root, startDragX, startDragY, startDragX + resizeDelta, startDragY + resizeDelta, true, true, units.gu(.5), 10);
325−
326− tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + resizeDelta, fakeWindow.resizeAreaMinWidth));
327− tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + resizeDelta, fakeWindow.resizeAreaMinHeight));
328−
329− // This will destroy the window and recreate it
330− windowLoader.active = false;
331− waitForRendering(root);
332− windowLoader.active = true;
333−
334− // Make sure its size is again the same as before
335− tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + resizeDelta, fakeWindow.resizeAreaMinWidth));
336− tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + resizeDelta, fakeWindow.resizeAreaMinHeight));
337− }
338−
339304 // This tests if dragging smaller than minSize and then larger again, will keep the edge sticking
340305 // to the mouse, instead of immediately making the window grow again when switching direction
341306 function test_resizeSmallerAndLarger_data() {
@@ -361,36 +326,6 @@ Item {
361326 tryCompare(fakeWindow, "height", initialWindowHeight);
362327 }
363328
364− function test_saveRestoreMaximized() {
365− var initialWindowX = fakeWindow.windowedX;
366− var initialWindowY = fakeWindow.windowedY;
367−
368− var moveDelta = units.gu(5);
369−
370− fakeWindow.windowedX = initialWindowX + moveDelta
371− fakeWindow.windowedY = initialWindowY + moveDelta
372−
373− // Now change the state to maximized. The window should not keep updating the stored values
374− fakeWindow.maximize()
375− fakeWindow.windowedX = 31415 // 0 is too risky to pass the test even when broken
376− fakeWindow.windowedY = 31415
377−
378− // This will destroy the window and recreate it
379− windowLoader.active = false;
380− waitForRendering(root);
381− windowLoader.active = true;
382−
383− // Make sure it's again where we left it in normal state before destroying
384− tryCompare(fakeWindow, "x", initialWindowX + moveDelta)
385− tryCompare(fakeWindow, "y", initialWindowX + moveDelta)
386−
387− // Make sure maximize() has been called after restoring
388− tryCompare(fakeWindow, "state", "maximized")
389−
390− // clean up
391− fakeWindow.restoreFromMaximized()
392− }
393−
394329 function test_restoreMovesIntoBounds_data() {
395330 return [
396331 {tag: "left off", x: -units.gu(5), y: units.gu(5), w: units.gu(10), h: units.gu(10)},
tests/qmltests/tst_Shell.qml+4−7
@@ -1857,14 +1857,11 @@ Rectangle {
18571857 var appRepeater = findChild(shell, "appRepeater");
18581858 var appDelegate = appRepeater.itemAt(0);
18591859
1860− var resizeArea = findChild(appDelegate, "windowResizeArea");
1861− var priv = findInvisibleChild(resizeArea, "priv");
1862−
18631860 // Make sure windows are at 0,0 or greater and they have a size that's > 0
1864− compare(priv.normalX >= 0, true)
1865− compare(priv.normalY >= 0, true)
1866− compare(priv.normalWidth > 0, true)
1867− compare(priv.normalHeight > 0, true)
1861+ compare(appDelegate.normalX >= 0, true)
1862+ compare(appDelegate.normalY >= 0, true)
1863+ compare(appDelegate.normalWidth > 0, true)
1864+ compare(appDelegate.normalHeight > 0, true)
18681865 }
18691866
18701867 // bug http://pad.lv/1431566