Merge lp:~mzanetti/unity8/saveRestoreWindowSizePosition into lp:unity8

Proposed by Michael Zanetti on 2015-01-28
Status: Merged
Approved by: Daniel d'Andrada on 2015-02-11
Approved revision: 1597
Merged at revision: 1605
Proposed branch: lp:~mzanetti/unity8/saveRestoreWindowSizePosition
Merge into: lp:unity8
Diff against target: 413 lines (+256/-22)
7 files modified
plugins/Utils/CMakeLists.txt (+2/-1)
plugins/Utils/plugin.cpp (+9/-0)
plugins/Utils/windowstatestorage.cpp (+96/-0)
plugins/Utils/windowstatestorage.h (+38/-0)
qml/Stages/DesktopStage.qml (+2/-2)
qml/Stages/WindowMoveResizeArea.qml (+17/-1)
tests/qmltests/Stages/tst_WindowMoveResizeArea.qml (+92/-18)
To merge this branch: bzr merge lp:~mzanetti/unity8/saveRestoreWindowSizePosition
Reviewer Review Type Date Requested Status
Gerry Boland Approve on 2015-02-12
PS Jenkins bot continuous-integration Approve on 2015-02-11
Daniel d'Andrada (community) 2015-01-28 Approve on 2015-02-11
Review via email: mp+247840@code.launchpad.net

Commit Message

save and restore window size and position

Description of the Change

 * Are there any related MPs required for this MP to build/function as expected? Please list.

no

 * Did you perform an exploratory manual test run of your code change and any related functionality?

yes

 * Did you make sure that your branch does not contain spurious tags?

yes

 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?

no

 * If you changed the UI, has there been a design review?

n/a

To post a comment you must log in.
Daniel d'Andrada (dandrader) wrote :

7 + * Copyright (C) 2014 Canonical, Ltd.

s/2014/2015

Daniel d'Andrada (dandrader) wrote :

247 + // This will destroy the window and recreate it
248 + windowLoader.active = false;
249 + windowLoader.active = true;

I was doing that in tst_Shell.qml in the past and found out that the loaded item wasn't really getting recreated (ie, current one destroyed and a brand new created). Check the shellLoader.itemDestroyed property and how it's used in tst_Shell.qml. You might need to do something similar to ensure you get a fresh new item.

Daniel d'Andrada (dandrader) wrote :

I find it confusing that you have two "fakeWindow" entities in the test:

"""
    property var fakeWindow: windowLoader.item
"""

and

"""
    Component {
        Rectangle {
            id: fakeWindow
            WindowMoveResizeArea {}
        }
    }
"""

Makes you wonder which one is being referred to in the code

Daniel d'Andrada (dandrader) wrote :

+ onReleased: {
+ WindowPositionStorage.savePosition(root.windowId,target.x, target.y, target.width, target.height)
+ }

I think it would be more efficient and symmetric if the position got saved in "Component.onDestruction:" instead. Would that work?

Gerry Boland (gerboland) wrote :

+ if(db !== null) return;
space after "if"

+ // db = LocalStorage.openDatabaseSync(identifier, version, description, estimated_size, callback(db))
comment for informative purposes?

+ tx.executeSql('CREATE TABLE IF NOT EXISTS settings
"settings" not a great table name for this

+ res = unefined;
typo

review: Needs Fixing
Daniel d'Andrada (dandrader) wrote :

On 28/01/15 14:05, Gerry Boland wrote:
> + // db = LocalStorage.openDatabaseSync(identifier, version, description, estimated_size, callback(db))
> comment for informative purposes?
I think so. I would rather keep it

Michael Zanetti (mzanetti) wrote :

> 247 + // This will destroy the window and recreate it
> 248 + windowLoader.active = false;
> 249 + windowLoader.active = true;
>
> I was doing that in tst_Shell.qml in the past and found out that the loaded
> item wasn't really getting recreated (ie, current one destroyed and a brand
> new created). Check the shellLoader.itemDestroyed property and how it's used
> in tst_Shell.qml. You might need to do something similar to ensure you get a
> fresh new item.

This seems to work fine for me... If I remove the saving code, the test fails as expected because the item is created at the default position. Also I use this in some apps and I'm quite confident that it does the destruction and recreation of the object.

Michael Zanetti (mzanetti) wrote :

> I find it confusing that you have two "fakeWindow" entities in the test:
>
> """
> property var fakeWindow: windowLoader.item
> """
>
> and
>
> """
> Component {
> Rectangle {
> id: fakeWindow
> WindowMoveResizeArea {}
> }
> }
> """
>
> Makes you wonder which one is being referred to in the code

Given that you can't reference things inside a Component from outside of it, I think it's quite obvious, isn't it?

Michael Zanetti (mzanetti) wrote :

> + onReleased: {
> + WindowPositionStorage.savePosition(root.windowId,target.x, target.y,
> target.width, target.height)
> + }
>
> I think it would be more efficient and symmetric if the position got saved in
> "Component.onDestruction:" instead. Would that work?

Seems a good point. I've tried it, but unfortunately it doesn't seem to work... First of all, the onDestruction signal behaves quite weird, for instance it doesn't get executed when the whole DesktopStage is destroyed, hence it is not triggered when we switch between Windowed and Staged mode. Also it would not save the state in case of a crash.

Michael Zanetti (mzanetti) wrote :

> + if(db !== null) return;
> space after "if"

fixed

> + // db = LocalStorage.openDatabaseSync(identifier, version, description,
> estimated_size, callback(db))
> comment for informative purposes?

yeah, I copy/pasted most of this code and it had this comment. I looked at it and thought this might be useful again. Looking at it again today it doesn't seem like that any more :D I've dropped it now.

>
> + tx.executeSql('CREATE TABLE IF NOT EXISTS settings
> "settings" not a great table name for this

fixed.

>
> + res = unefined;
> typo

fixed

Daniel d'Andrada (dandrader) wrote :

I just realized that the naming is slightly wrong.

We're saving the window geometry (size + position, expressed as a rectangle), not only its position.

thus:
s/WindowPositionStorage/WindowGeometryStorage
s/savePosition/saveGeometry
s/getPosition/getGeometry

review: Needs Fixing
MichaƂ Sawicz (saviq) wrote :

Make it "state" rather, we'll probably be saving more in there soon (like maximized / fullscreen etc.).

Daniel d'Andrada (dandrader) wrote :

Looks good.

Cherry on top would some UI control so that I could unload & load the window, therefore being able to manually test the state saving. But I won't block on that.

review: Approve
Daniel d'Andrada (dandrader) wrote :

* Did you perform an exploratory manual test run of the code change and any related functionality?
Yes

* Did CI run pass? If not, please explain why.
Unrelated autopilot failure

* Did you make sure that the branch does not contain spurious tags?
Yes, it's clean

Daniel d'Andrada (dandrader) wrote :

thumbs up

review: Approve
Daniel d'Andrada (dandrader) wrote :

Saving the window geometry to the database on MouseArea.release causes jerkiness as you can see in this video:
https://www.dropbox.com/s/ihiy05ssdtdcdys/shaky_window.ogv?dl=0

The issue goes away if I comment-out the "onReleased: WindowStateStorage.saveState()" code.

review: Needs Fixing
1591. By Michael Zanetti on 2015-02-10

make the moveResizeArea visible

1592. By Michael Zanetti on 2015-02-10

replace WindowStateStorage with a threaded c++ implementation

1593. By Michael Zanetti on 2015-02-10

drop localstorage qml plugin dep

1594. By Michael Zanetti on 2015-02-10

improve debug print

Michael Zanetti (mzanetti) wrote :

> Saving the window geometry to the database on MouseArea.release causes
> jerkiness as you can see in this video:
> https://www.dropbox.com/s/ihiy05ssdtdcdys/shaky_window.ogv?dl=0
>
> The issue goes away if I comment-out the "onReleased:
> WindowStateStorage.saveState()" code.

ok... this shouldn't happen any more...

1595. By Michael Zanetti on 2015-02-10

fix init code

Daniel d'Andrada (dandrader) wrote :

I'm getting that after every window movement or resize:

"""
QSqlQuery::exec: database not open
Error esecuting query "INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values ('test-window-id', '107', '210', '160', '160');" Driver error: "" Database error: ""
"""

review: Needs Fixing
Michael Zanetti (mzanetti) wrote :

> I'm getting that after every window movement or resize:
>
> """
> QSqlQuery::exec: database not open
> Error esecuting query "INSERT OR REPLACE INTO geometry (windowId, x, y, width,
> height) values ('test-window-id', '107', '210', '160', '160');" Driver error:
> "" Database error: ""
> """

Can you please post the error that gets printed on the open() call?

1596. By Michael Zanetti on 2015-02-11

create cache dir if it doesn't exist

Michael Zanetti (mzanetti) wrote :

> I'm getting that after every window movement or resize:
>
> """
> QSqlQuery::exec: database not open
> Error esecuting query "INSERT OR REPLACE INTO geometry (windowId, x, y, width,
> height) values ('test-window-id', '107', '210', '160', '160');" Driver error:
> "" Database error: ""
> """

found it... should be fixed now

Daniel d'Andrada (dandrader) wrote :

> > I'm getting that after every window movement or resize:
> >
> > """
> > QSqlQuery::exec: database not open
> > Error esecuting query "INSERT OR REPLACE INTO geometry (windowId, x, y,
> width,
> > height) values ('test-window-id', '107', '210', '160', '160');" Driver
> error:
> > "" Database error: ""
> > """
>
> found it... should be fixed now

Yes, it's smooth now. Thanks!

But I have to say that this incident scared me off. I seems too wasteful to me to do IO like that on every single window drag or resize.

You might say this is a temporary solution but I think we will be using it for a quite some time still.

I won't block this MP but I also don't feel comfortable enough to approve it. So let's have a second opinion on the subject.

review: Abstain
Daniel d'Andrada (dandrader) wrote :

I'm also slightly concerned that this is an ever-growing database

1597. By Michael Zanetti on 2015-02-11

only store when destroying the scene

Michael Zanetti (mzanetti) wrote :

> > > I'm getting that after every window movement or resize:
> > >
> > > """
> > > QSqlQuery::exec: database not open
> > > Error esecuting query "INSERT OR REPLACE INTO geometry (windowId, x, y,
> > width,
> > > height) values ('test-window-id', '107', '210', '160', '160');" Driver
> > error:
> > > "" Database error: ""
> > > """
> >
> > found it... should be fixed now
>
> Yes, it's smooth now. Thanks!
>
> But I have to say that this incident scared me off. I seems too wasteful to me
> to do IO like that on every single window drag or resize.
>
> You might say this is a temporary solution but I think we will be using it for
> a quite some time still.
>
> I won't block this MP but I also don't feel comfortable enough to approve it.
> So let's have a second opinion on the subject.

ok. changed it to only save it on destruction...

but seriously.. how often do you resize a window? I mean, I know it happens, but in average we're talking about multiple minutes between the events. I think it's worth it, in favour to not lose information on unclean shutdowns. Anyways... time will tell...

Daniel d'Andrada (dandrader) wrote :

> ok. changed it to only save it on destruction...

Thanks! Much appreciated!

>
> but seriously.. how often do you resize a window?

I find it hard to give you a number. But it hurts me to see resources being wasted like that when it's reasonably simple to avoid it. It's like seeing a faucet kept open, unattended, letting water go down the drain.

> I mean, I know it happens,
> but in average we're talking about multiple minutes between the events. I
> think it's worth it, in favour to not lose information on unclean shutdowns.
> Anyways... time will tell...

But then, how often do you get your window manager/compositor to crash and how bad is it to lose the windows' last position because of that?

I say rarely to the first and negligible to the second.

review: Approve
Gerry Boland (gerboland) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'plugins/Utils/CMakeLists.txt'
2--- plugins/Utils/CMakeLists.txt 2015-02-03 12:17:50 +0000
3+++ plugins/Utils/CMakeLists.txt 2015-02-11 12:05:36 +0000
4@@ -15,6 +15,7 @@
5 unitymenumodelpaths.cpp
6 windowkeysfilter.cpp
7 easingcurve.cpp
8+ windowstatestorage.cpp
9 plugin.cpp
10 )
11
12@@ -30,6 +31,6 @@
13 # files directly in targets.
14 set_target_properties(Utils-qml PROPERTIES COMPILE_FLAGS -fvisibility=default)
15
16-qt5_use_modules(Utils-qml Qml Quick DBus Network Gui)
17+qt5_use_modules(Utils-qml Qml Quick DBus Network Gui Sql Concurrent)
18
19 add_unity8_plugin(Utils 0.1 Utils TARGETS Utils-qml)
20
21=== modified file 'plugins/Utils/plugin.cpp'
22--- plugins/Utils/plugin.cpp 2014-12-19 14:51:35 +0000
23+++ plugins/Utils/plugin.cpp 2015-02-11 12:05:36 +0000
24@@ -33,6 +33,14 @@
25 #include "unitymenumodelpaths.h"
26 #include "windowkeysfilter.h"
27 #include "easingcurve.h"
28+#include "windowstatestorage.h"
29+
30+static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
31+{
32+ Q_UNUSED(engine)
33+ Q_UNUSED(scriptEngine)
34+ return new WindowStateStorage();
35+}
36
37 void UtilsPlugin::registerTypes(const char *uri)
38 {
39@@ -46,6 +54,7 @@
40 qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
41 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
42 qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
43+ qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
44 }
45
46 void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
47
48=== added file 'plugins/Utils/windowstatestorage.cpp'
49--- plugins/Utils/windowstatestorage.cpp 1970-01-01 00:00:00 +0000
50+++ plugins/Utils/windowstatestorage.cpp 2015-02-11 12:05:36 +0000
51@@ -0,0 +1,96 @@
52+/*
53+ * Copyright 2015 Canonical Ltd.
54+ *
55+ * This program is free software; you can redistribute it and/or modify
56+ * it under the terms of the GNU Lesser General Public License as published by
57+ * the Free Software Foundation; version 3.
58+ *
59+ * This program is distributed in the hope that it will be useful,
60+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
61+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
62+ * GNU Lesser General Public License for more details.
63+ *
64+ * You should have received a copy of the GNU Lesser General Public License
65+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
66+ */
67+
68+#include "windowstatestorage.h"
69+
70+#include <QtConcurrent>
71+#include <QDebug>
72+#include <QSqlQuery>
73+#include <QSqlError>
74+#include <QSqlResult>
75+#include <QRect>
76+
77+QMutex WindowStateStorage::s_mutex;
78+
79+WindowStateStorage::WindowStateStorage(QObject *parent):
80+ QObject(parent)
81+{
82+ QString dbPath = QDir::homePath() + "/.cache/unity8/";
83+ m_db = QSqlDatabase::addDatabase("QSQLITE");
84+ QDir dir;
85+ dir.mkpath(dbPath);
86+ m_db.setDatabaseName(dbPath + "windowstatestorage.sqlite");
87+ initdb();
88+}
89+
90+void WindowStateStorage::saveGeometry(const QString &windowId, const QRect &rect)
91+{
92+ QString queryString = QString("INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values ('%1', '%2', '%3', '%4', '%5');")
93+ .arg(windowId)
94+ .arg(rect.x())
95+ .arg(rect.y())
96+ .arg(rect.width())
97+ .arg(rect.height());
98+
99+ QtConcurrent::run(executeAsyncQuery, queryString);
100+}
101+
102+void WindowStateStorage::executeAsyncQuery(const QString &queryString)
103+{
104+ QMutexLocker l(&s_mutex);
105+ QSqlQuery query;
106+
107+ bool ok = query.exec(queryString);
108+ if (!ok) {
109+ qWarning() << "Error executing query" << queryString
110+ << "Driver error:" << query.lastError().driverText()
111+ << "Database error:" << query.lastError().databaseText();
112+ }
113+}
114+
115+QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue)
116+{
117+ QMutexLocker l(&s_mutex);
118+ QString queryString = QString("SELECT * FROM geometry WHERE windowId = '%1';")
119+ .arg(windowId);
120+ QSqlQuery query;
121+
122+ bool ok = query.exec(queryString);
123+ if (!ok) {
124+ qWarning() << "Error retrieving window state for" << windowId
125+ << "Driver error:" << query.lastError().driverText()
126+ << "Database error:" << query.lastError().databaseText();
127+ return defaultValue;
128+ }
129+ if (!query.first()) {
130+ return defaultValue;
131+ }
132+ return QRect(query.value("x").toInt(), query.value("y").toInt(), query.value("width").toInt(), query.value("height").toInt());
133+}
134+
135+void WindowStateStorage::initdb()
136+{
137+ m_db.open();
138+ if (!m_db.open()) {
139+ qWarning() << "Error opening state database:" << m_db.lastError().driverText() << m_db.lastError().databaseText();
140+ return;
141+ }
142+
143+ if (!m_db.tables().contains("geometry")) {
144+ QSqlQuery query;
145+ query.exec("CREATE TABLE geometry(windowId TEXT UNIQUE, x INTEGER, y INTEGER, width INTEGER, height INTEGER);");
146+ }
147+}
148
149=== added file 'plugins/Utils/windowstatestorage.h'
150--- plugins/Utils/windowstatestorage.h 1970-01-01 00:00:00 +0000
151+++ plugins/Utils/windowstatestorage.h 2015-02-11 12:05:36 +0000
152@@ -0,0 +1,38 @@
153+/*
154+ * Copyright 2015 Canonical Ltd.
155+ *
156+ * This program is free software; you can redistribute it and/or modify
157+ * it under the terms of the GNU Lesser General Public License as published by
158+ * the Free Software Foundation; version 3.
159+ *
160+ * This program is distributed in the hope that it will be useful,
161+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
162+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
163+ * GNU Lesser General Public License for more details.
164+ *
165+ * You should have received a copy of the GNU Lesser General Public License
166+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
167+ */
168+
169+#include <QObject>
170+#include <QSqlDatabase>
171+#include <QMutex>
172+
173+class WindowStateStorage: public QObject
174+{
175+ Q_OBJECT
176+public:
177+ WindowStateStorage(QObject *parent = 0);
178+
179+ Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
180+ Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue);
181+
182+private:
183+ void initdb();
184+
185+ static void executeAsyncQuery(const QString &queryString);
186+ static QMutex s_mutex;
187+
188+ // NB: This is accessed from threads. Make sure to mutex it.
189+ QSqlDatabase m_db;
190+};
191
192=== modified file 'qml/Stages/DesktopStage.qml'
193--- qml/Stages/DesktopStage.qml 2014-12-16 11:55:20 +0000
194+++ qml/Stages/DesktopStage.qml 2015-02-11 12:05:36 +0000
195@@ -38,8 +38,6 @@
196 Connections {
197 target: ApplicationManager
198 onApplicationAdded: {
199- // Initial placement to avoid having the window decoration behind the panel
200- appRepeater.itemAt(ApplicationManager.count-1).y = units.gu(3)
201 ApplicationManager.requestFocusApplication(ApplicationManager.get(ApplicationManager.count-1).appId)
202 }
203
204@@ -91,6 +89,7 @@
205 delegate: Item {
206 id: appDelegate
207 z: ApplicationManager.count - index
208+ y: units.gu(3)
209 width: units.gu(60)
210 height: units.gu(50)
211
212@@ -121,6 +120,7 @@
213 minWidth: appDelegate.minWidth
214 minHeight: appDelegate.minHeight
215 resizeHandleWidth: units.gu(0.5)
216+ windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
217
218 onPressed: ApplicationManager.requestFocusApplication(model.appId)
219 }
220
221=== modified file 'qml/Stages/WindowMoveResizeArea.qml'
222--- qml/Stages/WindowMoveResizeArea.qml 2014-12-18 15:03:24 +0000
223+++ qml/Stages/WindowMoveResizeArea.qml 2015-02-11 12:05:36 +0000
224@@ -18,6 +18,7 @@
225
226 import QtQuick 2.3
227 import Ubuntu.Components 1.1
228+import Utils 0.1
229
230 MouseArea {
231 id: root
232@@ -26,7 +27,8 @@
233
234 // The target item managed by this. Must be a parent or a sibling
235 // The area will anchor to it and manage move and resize events
236- property var target: null
237+ property Item target: null
238+ property string windowId: ""
239 property int resizeHandleWidth: 0
240 property int minWidth: 0
241 property int minHeight: 0
242@@ -47,6 +49,16 @@
243
244 }
245
246+ Component.onCompleted: {
247+ var windowState = WindowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
248+ if (windowState !== undefined) {
249+ target.x = windowState.x
250+ target.y = windowState.y
251+ target.width = windowState.width
252+ target.height = windowState.height
253+ }
254+ }
255+
256 onPressed: {
257 priv.startPoint = mapToItem(null, Qt.point(mouse.x, mouse.y)).x;
258 priv.startWidth = root.width;
259@@ -95,4 +107,8 @@
260 target.y += mouseDiff.y;
261 }
262 }
263+
264+ Component.onDestruction: {
265+ WindowStateStorage.saveGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
266+ }
267 }
268
269=== modified file 'tests/qmltests/Stages/tst_WindowMoveResizeArea.qml'
270--- tests/qmltests/Stages/tst_WindowMoveResizeArea.qml 2014-12-16 11:49:46 +0000
271+++ tests/qmltests/Stages/tst_WindowMoveResizeArea.qml 2015-02-11 12:05:36 +0000
272@@ -1,5 +1,5 @@
273 /*
274- * Copyright 2014 Canonical Ltd.
275+ * Copyright 2014-2015 Canonical Ltd.
276 *
277 * This program is free software; you can redistribute it and/or modify
278 * it under the terms of the GNU General Public License as published by
279@@ -29,20 +29,46 @@
280 height: units.gu(60)
281 width: units.gu(60)
282
283- Rectangle {
284- id: fakeWindow
285- height: units.gu(20)
286- width: units.gu(20)
287- color: "khaki"
288- WindowMoveResizeArea {
289- id: moveResizeArea
290- target: fakeWindow
291- resizeHandleWidth: units.gu(0.5)
292- minWidth: units.gu(15)
293- minHeight: units.gu(10)
294+ property var fakeWindow: windowLoader.item
295+
296+ Component {
297+ id: fakeWindowComponent
298+
299+ Item {
300+ id: fakeWindow
301+ property alias minWidth: moveResizeArea.minWidth
302+ property alias minHeight: moveResizeArea.minHeight
303+ x: units.gu(20)
304+ y: units.gu(20)
305+ height: units.gu(20)
306+ width: units.gu(20)
307+
308+ WindowMoveResizeArea {
309+ id: moveResizeArea
310+ target: fakeWindow
311+ resizeHandleWidth: units.gu(0.5)
312+ minWidth: units.gu(15)
313+ minHeight: units.gu(10)
314+ windowId: "test-window-id"
315+ }
316+
317+ Rectangle {
318+ anchors.fill: moveResizeArea
319+ color: "red"
320+ }
321+
322+ Rectangle {
323+ anchors.fill: fakeWindow
324+ color: "blue"
325+ }
326 }
327 }
328
329+ Loader {
330+ id: windowLoader
331+ sourceComponent: fakeWindowComponent
332+ }
333+
334 UT.UnityTestCase {
335 when: windowShown
336
337@@ -100,8 +126,8 @@
338 var startDragY = initialWindowY + initialWindowHeight + 1
339 mouseFlick(root, startDragX, startDragY, startDragX + data.dx, startDragY + data.dy, true, true, units.gu(.5), 10);
340
341- tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + data.dx, moveResizeArea.minWidth));
342- tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + data.dy, moveResizeArea.minHeight));
343+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + data.dx, fakeWindow.minWidth));
344+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + data.dy, fakeWindow.minHeight));
345
346 compare(fakeWindow.x, initialWindowX);
347 compare(fakeWindow.y, initialWindowY);
348@@ -126,13 +152,61 @@
349 var startDragY = initialWindowY - 1
350 mouseFlick(root, startDragX, startDragY, startDragX + data.dx, startDragY + data.dy, true, true, units.gu(.5), 10);
351
352- tryCompare(fakeWindow, "width", Math.max(initialWindowWidth - data.dx, moveResizeArea.minWidth));
353- tryCompare(fakeWindow, "height", Math.max(initialWindowHeight - data.dy, moveResizeArea.minHeight));
354+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth - data.dx, fakeWindow.minWidth));
355+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight - data.dy, fakeWindow.minHeight));
356
357- var maxMoveX = initialWindowWidth - moveResizeArea.minWidth;
358- var maxMoveY = initialWindowHeight - moveResizeArea.minHeight;
359+ var maxMoveX = initialWindowWidth - fakeWindow.minWidth;
360+ var maxMoveY = initialWindowHeight - fakeWindow.minHeight;
361 compare(fakeWindow.x, Math.min(initialWindowX + data.dx, initialWindowX + maxMoveX));
362 compare(fakeWindow.y, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));
363 }
364+
365+ function test_saveRestorePosition() {
366+ var initialWindowX = fakeWindow.x;
367+ var initialWindowY = fakeWindow.y;
368+ var initialWindowWidth = fakeWindow.width;
369+ var initialWindowHeight = fakeWindow.height;
370+
371+ var moveDelta = units.gu(5);
372+ var startDragX = initialWindowX + fakeWindow.width / 2;
373+ var startDragY = initialWindowY + fakeWindow.height / 2;
374+ mouseFlick(root, startDragX, startDragY, startDragX + moveDelta, startDragY + moveDelta, true, true, units.gu(.5), 10)
375+
376+ tryCompare(fakeWindow, "x", initialWindowX + moveDelta)
377+ tryCompare(fakeWindow, "y", initialWindowX + moveDelta)
378+
379+ // This will destroy the window and recreate it
380+ windowLoader.active = false;
381+ waitForRendering(root);
382+ windowLoader.active = true;
383+
384+ // Make sure it's again where we left it before destroying
385+ tryCompare(fakeWindow, "x", initialWindowX + moveDelta)
386+ tryCompare(fakeWindow, "y", initialWindowX + moveDelta)
387+ }
388+
389+ function test_saveRestoreSize() {
390+ var initialWindowX = fakeWindow.x;
391+ var initialWindowY = fakeWindow.y;
392+ var initialWindowWidth = fakeWindow.width
393+ var initialWindowHeight = fakeWindow.height
394+
395+ var resizeDelta = units.gu(5)
396+ var startDragX = initialWindowX + initialWindowWidth + 1
397+ var startDragY = initialWindowY + initialWindowHeight + 1
398+ mouseFlick(root, startDragX, startDragY, startDragX + resizeDelta, startDragY + resizeDelta, true, true, units.gu(.5), 10);
399+
400+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + resizeDelta, fakeWindow.minWidth));
401+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + resizeDelta, fakeWindow.minHeight));
402+
403+ // This will destroy the window and recreate it
404+ windowLoader.active = false;
405+ waitForRendering(root);
406+ windowLoader.active = true;
407+
408+ // Make sure its size is again the same as before
409+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + resizeDelta, fakeWindow.minWidth));
410+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + resizeDelta, fakeWindow.minHeight));
411+ }
412 }
413 }

Subscribers

People subscribed via source and target branches