Merge lp:~mzanetti/unity8/panel-button-fixes into lp:unity8

Proposed by Michael Zanetti
Status: Merged
Approved by: Lukáš Tinkl
Approved revision: 1993
Merged at revision: 2029
Proposed branch: lp:~mzanetti/unity8/panel-button-fixes
Merge into: lp:unity8
Prerequisite: lp:~unity-team/unity8/mousePointer
Diff against target: 595 lines (+287/-40)
11 files modified
plugins/Utils/windowstatestorage.cpp (+62/-23)
plugins/Utils/windowstatestorage.h (+13/-1)
qml/Components/WindowControlButtons.qml (+3/-0)
qml/Panel/Panel.qml (+5/-3)
qml/Shell.qml (+2/-0)
qml/Stages/DesktopStage.qml (+10/-3)
qml/Stages/WindowResizeArea.qml (+41/-8)
tests/mocks/Utils/windowstatestorage.cpp (+17/-0)
tests/mocks/Utils/windowstatestorage.h (+12/-0)
tests/qmltests/Stages/tst_WindowResizeArea.qml (+35/-2)
tests/qmltests/tst_Shell.qml (+87/-0)
To merge this branch: bzr merge lp:~mzanetti/unity8/panel-button-fixes
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Lukáš Tinkl (community) Approve
Michał Sawicz Pending
Review via email: mp+273838@code.launchpad.net

This proposal supersedes a proposal from 2015-09-28.

Commit message

Fixes for the panel buttons

* Hide panel buttons when switching to staged mode
* properly save/restore window states, not just geometry

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?

n/a

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

n/a

To post a comment you must log in.
Revision history for this message
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

It should conflict BIG TIME with mousePointer as it changes the resize area as well

Revision history for this message
Michael Zanetti (mzanetti) wrote : Posted in a previous version of this proposal

ok, will redo it on top of that

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:1982
http://jenkins.qa.ubuntu.com/job/unity8-ci/6352/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-vivid-touch/4365
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-wily-touch/727
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/unity-phablet-qmluitests-vivid/1064
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/unity-phablet-qmluitests-wily/382
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-vivid-amd64-ci/959
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-vivid-i386-ci/960
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-wily-amd64-ci/591
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-wily-i386-ci/592
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-vivid-mako/3557
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/4362
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/4362/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/23682
    UNSTABLE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-wily-mako/438
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-wily-armhf/727
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-wily-armhf/727/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/23681

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/unity8-ci/6352/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote : Posted in a previous version of this proposal

> It should conflict BIG TIME with mousePointer as it changes the resize area as
> well

ok... it really wasn't that BIG TIME but it's rebased on top of that now :)

Revision history for this message
Michael Zanetti (mzanetti) wrote : Posted in a previous version of this proposal

There are some tests failing. Some of them fail because of the prereq branches. I'll update this when the prereqs are fixed.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal

Text conflict in debian/control
Text conflict in plugins/Utils/windowstatestorage.cpp
Text conflict in qml/OrientedShell.qml
3 conflicts encountered.

review: Needs Fixing
Revision history for this message
Michael Zanetti (mzanetti) wrote : Posted in a previous version of this proposal

> Text conflict in debian/control
> Text conflict in plugins/Utils/windowstatestorage.cpp
> Text conflict in qml/OrientedShell.qml
> 3 conflicts encountered.

They're to be resolved in the prerequisite (mousePointer)

Revision history for this message
Michał Sawicz (saviq) : Posted in a previous version of this proposal
review: Abstain
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote : Posted in a previous version of this proposal

Text conflict in CMakeLists.txt
1 conflicts encountered.

Revision history for this message
Lukáš Tinkl (lukas-kde) wrote : Posted in a previous version of this proposal

Some inline comments

review: Needs Fixing
1989. By Michael Zanetti

fix some review notes

Revision history for this message
Michael Zanetti (mzanetti) wrote :

> Some inline comments

fixed

Revision history for this message
Lukáš Tinkl (lukas-kde) wrote :

Just for the record:

animationsEnabled = (animated === undefined) | animated;

this should use a logical "or", not a bitwise

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1990. By Michael Zanetti

fix default arg handling

Revision history for this message
Michael Zanetti (mzanetti) wrote :

> Just for the record:
>
> animationsEnabled = (animated === undefined) | animated;
>
> this should use a logical "or", not a bitwise

oops. fixed, thanks!

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1991. By Michael Zanetti

merge with prereq

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Lukáš Tinkl (lukas-kde) wrote :

The decoration buttons are now visible on the locked screen, right after start up (at which point there shall be no window open).

review: Needs Fixing
1992. By Michael Zanetti

make sure panel buttons are not visible while greeter is shown

1993. By Michael Zanetti

drop debug print

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Lukáš Tinkl (lukas-kde) wrote :

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

Yes, works fine

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

Nope, unity-api mismatch probably

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

Yes

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1994. By Michael Zanetti

merge trunk

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1995. By Michael Zanetti

fix tests (clear the mock state store between tests)

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1996. By Michael Zanetti

fix initialisation of window storage state for new apps/windows

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:1996
http://jenkins.qa.ubuntu.com/job/unity8-ci/6618/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-vivid-touch/4964
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-xenial-touch/33/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity-phablet-qmluitests-vivid/1330
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-qmluitest-xenial-amd64/33
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-vivid-amd64-ci/1225
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-vivid-i386-ci/1226
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-xenial-amd64-ci/32
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity8-xenial-i386-ci/32
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-vivid-mako/3987
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/4961
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-vivid-armhf/4961/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/24785
    ABORTED: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-xenial-mako/22/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-xenial-armhf/33
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-xenial-armhf/33/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/24784

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/unity8-ci/6618/rebuild

review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'plugins/Utils/windowstatestorage.cpp'
2--- plugins/Utils/windowstatestorage.cpp 2015-09-14 09:11:08 +0000
3+++ plugins/Utils/windowstatestorage.cpp 2015-10-29 10:35:08 +0000
4@@ -47,26 +47,38 @@
5 m_db.close();
6 }
7
8+void WindowStateStorage::saveState(const QString &windowId, WindowStateStorage::WindowState state)
9+{
10+ const QString queryString = QStringLiteral("INSERT OR REPLACE INTO state (windowId, state) values ('%1', '%2');")
11+ .arg(windowId)
12+ .arg((int)state);
13+
14+ saveValue(queryString);
15+}
16+
17+WindowStateStorage::WindowState WindowStateStorage::getState(const QString &windowId, WindowStateStorage::WindowState defaultValue) const
18+{
19+ const QString queryString = QStringLiteral("SELECT * FROM state WHERE windowId = '%1';")
20+ .arg(windowId);
21+
22+ QSqlQuery query = getValue(queryString);
23+
24+ if (!query.first()) {
25+ return defaultValue;
26+ }
27+ return (WindowState)query.value("state").toInt();
28+}
29+
30 void WindowStateStorage::saveGeometry(const QString &windowId, const QRect &rect)
31 {
32- QMutexLocker mutexLocker(&s_mutex);
33-
34- QString queryString = QStringLiteral("INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values ('%1', '%2', '%3', '%4', '%5');")
35+ const QString queryString = QStringLiteral("INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values ('%1', '%2', '%3', '%4', '%5');")
36 .arg(windowId)
37 .arg(rect.x())
38 .arg(rect.y())
39 .arg(rect.width())
40 .arg(rect.height());
41
42- QFuture<void> future = QtConcurrent::run(executeAsyncQuery, queryString);
43- m_asyncQueries.append(future);
44-
45- QFutureWatcher<void> *futureWatcher = new QFutureWatcher<void>();
46- futureWatcher->setFuture(future);
47- connect(futureWatcher, &QFutureWatcher<void>::finished,
48- this,
49- [=](){ m_asyncQueries.removeAll(futureWatcher->future());
50- futureWatcher->deleteLater(); });
51+ saveValue(queryString);
52 }
53
54 void WindowStateStorage::executeAsyncQuery(const QString &queryString)
55@@ -82,20 +94,13 @@
56 }
57 }
58
59-QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue)
60+QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue) const
61 {
62- QMutexLocker l(&s_mutex);
63 QString queryString = QStringLiteral("SELECT * FROM geometry WHERE windowId = '%1';")
64 .arg(windowId);
65- QSqlQuery query;
66-
67- bool ok = query.exec(queryString);
68- if (!ok) {
69- qWarning() << "Error retrieving window state for" << windowId
70- << "Driver error:" << query.lastError().driverText()
71- << "Database error:" << query.lastError().databaseText();
72- return defaultValue;
73- }
74+
75+ QSqlQuery query = getValue(queryString);
76+
77 if (!query.first()) {
78 return defaultValue;
79 }
80@@ -114,4 +119,38 @@
81 QSqlQuery query;
82 query.exec(QStringLiteral("CREATE TABLE geometry(windowId TEXT UNIQUE, x INTEGER, y INTEGER, width INTEGER, height INTEGER);"));
83 }
84+
85+ if (!m_db.tables().contains("state")) {
86+ QSqlQuery query;
87+ query.exec("CREATE TABLE state(windowId TEXT UNIQUE, state INTEGER);");
88+ }
89+}
90+
91+void WindowStateStorage::saveValue(const QString &queryString)
92+{
93+ QMutexLocker mutexLocker(&s_mutex);
94+
95+ QFuture<void> future = QtConcurrent::run(executeAsyncQuery, queryString);
96+ m_asyncQueries.append(future);
97+
98+ QFutureWatcher<void> *futureWatcher = new QFutureWatcher<void>();
99+ futureWatcher->setFuture(future);
100+ connect(futureWatcher, &QFutureWatcher<void>::finished,
101+ this,
102+ [=](){ m_asyncQueries.removeAll(futureWatcher->future());
103+ futureWatcher->deleteLater(); });
104+}
105+
106+QSqlQuery WindowStateStorage::getValue(const QString &queryString) const
107+{
108+ QMutexLocker l(&s_mutex);
109+ QSqlQuery query;
110+
111+ bool ok = query.exec(queryString);
112+ if (!ok) {
113+ qWarning() << "Error retrieving database query:" << queryString
114+ << "Driver error:" << query.lastError().driverText()
115+ << "Database error:" << query.lastError().databaseText();
116+ }
117+ return query;
118 }
119
120=== modified file 'plugins/Utils/windowstatestorage.h'
121--- plugins/Utils/windowstatestorage.h 2015-03-13 19:01:32 +0000
122+++ plugins/Utils/windowstatestorage.h 2015-10-29 10:35:08 +0000
123@@ -22,16 +22,28 @@
124 class WindowStateStorage: public QObject
125 {
126 Q_OBJECT
127+ Q_ENUMS(WindowState)
128 public:
129+ enum WindowState {
130+ WindowStateNormal,
131+ WindowStateMaximized
132+ };
133+
134 WindowStateStorage(QObject *parent = 0);
135 virtual ~WindowStateStorage();
136
137+ Q_INVOKABLE void saveState(const QString &windowId, WindowState state);
138+ Q_INVOKABLE WindowState getState(const QString &windowId, WindowState defaultValue) const;
139+
140 Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
141- Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue);
142+ Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue) const;
143
144 private:
145 void initdb();
146
147+ void saveValue(const QString &queryString);
148+ QSqlQuery getValue(const QString &queryString) const;
149+
150 static void executeAsyncQuery(const QString &queryString);
151 static QMutex s_mutex;
152
153
154=== modified file 'qml/Components/WindowControlButtons.qml'
155--- qml/Components/WindowControlButtons.qml 2014-11-24 11:21:38 +0000
156+++ qml/Components/WindowControlButtons.qml 2015-10-29 10:35:08 +0000
157@@ -28,6 +28,7 @@
158 signal maximize()
159
160 Rectangle {
161+ objectName: "closeWindowButton"
162 height: parent.height; width: height; radius: height / 2
163 gradient: Gradient {
164 GradientStop { color: "#F49073"; position: 0 }
165@@ -38,6 +39,7 @@
166 MouseArea { anchors.fill: parent; onClicked: root.close() }
167 }
168 Rectangle {
169+ objectName: "minimizeWindowButton"
170 height: parent.height; width: height; radius: height / 2
171 gradient: Gradient {
172 GradientStop { color: "#92918C"; position: 0 }
173@@ -48,6 +50,7 @@
174 MouseArea { anchors.fill: parent; onClicked: root.minimize() }
175 }
176 Rectangle {
177+ objectName: "maximizeWindowButton"
178 height: parent.height; width: height; radius: height / 2
179 gradient: Gradient {
180 GradientStop { color: "#92918C"; position: 0 }
181
182=== modified file 'qml/Panel/Panel.qml'
183--- qml/Panel/Panel.qml 2015-09-29 12:48:46 +0000
184+++ qml/Panel/Panel.qml 2015-10-29 10:35:08 +0000
185@@ -28,6 +28,7 @@
186 property alias callHint: __callHint
187 property bool fullscreenMode: false
188 property real indicatorAreaShowProgress: 1.0
189+ property bool locked: false
190
191 opacity: fullscreenMode && indicators.fullyClosed ? 0.0 : 1.0
192
193@@ -136,7 +137,7 @@
194 }
195
196 shown: false
197- width: root.width - (PanelState.buttonsVisible ? windowControlButtons.width : 0)
198+ width: root.width - (windowControlButtons.visible ? windowControlButtons.width : 0)
199 minimizedPanelHeight: units.gu(3)
200 expandedPanelHeight: units.gu(7)
201 openedHeight: root.height - indicatorOrangeLine.height
202@@ -164,13 +165,14 @@
203
204 WindowControlButtons {
205 id: windowControlButtons
206+ objectName: "panelWindowControlButtons"
207 anchors {
208 left: parent.left
209 top: parent.top
210 margins: units.gu(0.7)
211 }
212 height: indicators.minimizedPanelHeight - anchors.margins * 2
213- visible: PanelState.buttonsVisible
214+ visible: PanelState.buttonsVisible && !root.locked
215 onClose: PanelState.close()
216 onMinimize: PanelState.minimize()
217 onMaximize: PanelState.maximize()
218@@ -189,7 +191,7 @@
219 id: __callHint
220 anchors {
221 top: parent.top
222- left: PanelState.buttonsVisible ? windowControlButtons.right : parent.left
223+ left: windowControlButtons.visible ? windowControlButtons.right : parent.left
224 }
225 height: indicators.minimizedPanelHeight
226 visible: active && indicators.state == "initial"
227
228=== modified file 'qml/Shell.qml'
229--- qml/Shell.qml 2015-10-16 17:11:54 +0000
230+++ qml/Shell.qml 2015-10-29 10:35:08 +0000
231@@ -57,6 +57,7 @@
232 property bool beingResized
233 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
234 property string mode: "full-greeter"
235+ property bool cursorVisible: false
236 function updateFocusedAppOrientation() {
237 applicationsDisplayLoader.item.updateFocusedAppOrientation();
238 }
239@@ -536,6 +537,7 @@
240
241 fullscreenMode: (topmostApplicationIsFullscreen && !lightDM.greeter.active && launcher.progress == 0)
242 || greeter.hasLockedApp
243+ locked: greeter && greeter.active
244 }
245
246 Launcher {
247
248=== modified file 'qml/Stages/DesktopStage.qml'
249--- qml/Stages/DesktopStage.qml 2015-10-19 14:27:57 +0000
250+++ qml/Stages/DesktopStage.qml 2015-10-29 10:35:08 +0000
251@@ -111,6 +111,7 @@
252 property: "buttonsVisible"
253 value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.state === "maximized"
254 }
255+ Component.onDestruction: PanelState.buttonsVisible = false;
256
257 FocusScope {
258 id: appContainer
259@@ -140,6 +141,7 @@
260
261 property bool maximized: false
262 property bool minimized: false
263+ property bool animationsEnabled: true
264
265 onFocusChanged: {
266 if (focus && ApplicationManager.focusedApplicationId !== model.appId) {
267@@ -158,15 +160,18 @@
268 value: ApplicationInfoInterface.RequestedRunning // Always running for now
269 }
270
271- function maximize() {
272+ function maximize(animated) {
273+ animationsEnabled = (animated === undefined) || animated;
274 minimized = false;
275 maximized = true;
276 }
277- function minimize() {
278+ function minimize(animated) {
279+ animationsEnabled = (animated === undefined) || animated;
280 maximized = false;
281 minimized = true;
282 }
283- function unmaximize() {
284+ function unmaximize(animated) {
285+ animationsEnabled = (animated === undefined) || animated;
286 minimized = false;
287 maximized = false;
288 }
289@@ -188,6 +193,7 @@
290 Transition {
291 from: "maximized,minimized,normal,"
292 to: "maximized,minimized,normal,"
293+ enabled: appDelegate.animationsEnabled
294 PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
295 },
296 Transition {
297@@ -213,6 +219,7 @@
298 }
299
300 WindowResizeArea {
301+ objectName: "windowResizeArea"
302 target: appDelegate
303 minWidth: units.gu(10)
304 minHeight: units.gu(10)
305
306=== modified file 'qml/Stages/WindowResizeArea.qml'
307--- qml/Stages/WindowResizeArea.qml 2015-09-29 12:48:46 +0000
308+++ qml/Stages/WindowResizeArea.qml 2015-10-29 10:35:08 +0000
309@@ -36,18 +36,51 @@
310 property int minWidth: 0
311 property int minHeight: 0
312
313+ QtObject {
314+ id: priv
315+ objectName: "priv"
316+
317+ property int normalX: 0
318+ property int normalY: 0
319+ property int normalWidth: 0
320+ property int normalHeight: 0
321+
322+ function updateNormalGeometry() {
323+ if (root.target.state == "normal") {
324+ normalX = root.target.x
325+ normalY = root.target.y
326+ normalWidth = root.target.width
327+ normalHeight = root.target.height
328+ }
329+ }
330+ }
331+
332+ Connections {
333+ target: root.target
334+ onXChanged: priv.updateNormalGeometry();
335+ onYChanged: priv.updateNormalGeometry();
336+ onWidthChanged: priv.updateNormalGeometry();
337+ onHeightChanged: priv.updateNormalGeometry();
338+ }
339+
340 Component.onCompleted: {
341- var windowState = windowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
342- if (windowState !== undefined) {
343- target.x = windowState.x
344- target.y = windowState.y
345- target.width = windowState.width
346- target.height = windowState.height
347- }
348+ var windowGeometry = windowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
349+ if (windowGeometry !== undefined) {
350+ target.x = windowGeometry.x
351+ target.y = windowGeometry.y
352+ target.width = windowGeometry.width
353+ target.height = windowGeometry.height
354+ }
355+ var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
356+ if (windowState === WindowStateStorage.WindowStateMaximized) {
357+ target.maximize(false)
358+ }
359+ priv.updateNormalGeometry();
360 }
361
362 Component.onDestruction: {
363- windowStateStorage.saveGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
364+ windowStateStorage.saveState(root.windowId, target.state == "maximized" ? WindowStateStorage.WindowStateMaximized : WindowStateStorage.WindowStateNormal)
365+ windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight))
366 }
367
368 QtObject {
369
370=== modified file 'tests/mocks/Utils/windowstatestorage.cpp'
371--- tests/mocks/Utils/windowstatestorage.cpp 2015-08-24 15:39:53 +0000
372+++ tests/mocks/Utils/windowstatestorage.cpp 2015-10-29 10:35:08 +0000
373@@ -44,3 +44,20 @@
374 if (!m_geometry.contains(windowId)) return defaultValue;
375 return m_geometry.value(windowId).toRect();
376 }
377+
378+void WindowStateStorage::clear()
379+{
380+ m_state.clear();
381+ m_geometry.clear();
382+}
383+
384+void WindowStateStorage::saveState(const QString &windowId, WindowState state)
385+{
386+ m_state[windowId] = state;
387+}
388+
389+WindowStateStorage::WindowState WindowStateStorage::getState(const QString &windowId, WindowStateStorage::WindowState defaultValue)
390+{
391+ if (!m_state.contains(windowId)) return defaultValue;
392+ return m_state.value(windowId);
393+}
394
395=== modified file 'tests/mocks/Utils/windowstatestorage.h'
396--- tests/mocks/Utils/windowstatestorage.h 2015-08-24 15:39:53 +0000
397+++ tests/mocks/Utils/windowstatestorage.h 2015-10-29 10:35:08 +0000
398@@ -22,12 +22,23 @@
399 {
400 Q_OBJECT
401 Q_PROPERTY(QVariantMap geometry READ geometry WRITE setGeometry NOTIFY geometryChanged)
402+ Q_ENUMS(WindowState)
403 public:
404+ enum WindowState {
405+ WindowStateNormal,
406+ WindowStateMaximized
407+ };
408 WindowStateStorage(QObject *parent = 0);
409
410+ Q_INVOKABLE void saveState(const QString &windowId, WindowState state);
411+ Q_INVOKABLE WindowState getState(const QString &windowId, WindowState defaultValue);
412+
413 Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
414 Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue);
415
416+ // Only in the mock, to easily restore a fresh state
417+ Q_INVOKABLE void clear();
418+
419 Q_SIGNALS:
420 void geometryChanged(const QVariantMap& geometry);
421
422@@ -35,5 +46,6 @@
423 void setGeometry(const QVariantMap& geometry);
424 QVariantMap geometry() const;
425
426+ QHash<QString, WindowState> m_state;
427 QVariantMap m_geometry;
428 };
429
430=== modified file 'tests/qmltests/Stages/tst_WindowResizeArea.qml'
431--- tests/qmltests/Stages/tst_WindowResizeArea.qml 2015-10-08 14:27:12 +0000
432+++ tests/qmltests/Stages/tst_WindowResizeArea.qml 2015-10-29 10:35:08 +0000
433@@ -44,8 +44,11 @@
434 width: units.gu(20)
435 property int windowHeight: height
436 property int windowWidth: width
437- onWindowHeightChanged: height = windowHeight
438- onWindowWidthChanged: width = windowWidth
439+ state: "normal"
440+
441+ function maximize() {
442+ state = "maximized"
443+ }
444
445 WindowResizeArea {
446 id: windowResizeArea
447@@ -195,5 +198,35 @@
448 tryCompare(fakeWindow, "width", initialWindowWidth);
449 tryCompare(fakeWindow, "height", initialWindowHeight);
450 }
451+
452+ function test_saveRestoreMaximized() {
453+ var initialWindowX = fakeWindow.x;
454+ var initialWindowY = fakeWindow.y;
455+
456+ var moveDelta = units.gu(5);
457+
458+ fakeWindow.x = initialWindowX + moveDelta
459+ fakeWindow.y = initialWindowY + moveDelta
460+
461+ // Now change the state to maximized. The window should not keep updating the stored values
462+ fakeWindow.state = "maximized"
463+ fakeWindow.x = 31415 // 0 is too risky to pass the test even when broken
464+ fakeWindow.y = 31415
465+
466+ // This will destroy the window and recreate it
467+ windowLoader.active = false;
468+ waitForRendering(root);
469+ windowLoader.active = true;
470+
471+ // Make sure it's again where we left it in normal state before destroying
472+ tryCompare(fakeWindow, "x", initialWindowX + moveDelta)
473+ tryCompare(fakeWindow, "y", initialWindowX + moveDelta)
474+
475+ // Make sure maximize() has been called after restoring
476+ tryCompare(fakeWindow, "state", "maximized")
477+
478+ // clean up
479+ fakeWindow.state = "normal"
480+ }
481 }
482 }
483
484=== modified file 'tests/qmltests/tst_Shell.qml'
485--- tests/qmltests/tst_Shell.qml 2015-10-20 08:10:03 +0000
486+++ tests/qmltests/tst_Shell.qml 2015-10-29 10:35:08 +0000
487@@ -29,8 +29,10 @@
488 import Unity.Test 0.1
489 import Powerd 0.1
490 import Wizard 0.1 as Wizard
491+import Utils 0.1
492
493 import "../../qml"
494+import "../../qml/Components/PanelState"
495 import "Stages"
496
497 Rectangle {
498@@ -302,6 +304,7 @@
499 mouseEmulation.checked = true;
500 tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
501 tearDown();
502+ WindowStateStorage.clear();
503 }
504
505 function loadShell(formFactor) {
506@@ -1707,5 +1710,89 @@
507
508 keyRelease(Qt.Key_Control);
509 }
510+
511+ // regression test for http://pad.lv/1443319
512+ function test_closeMaximizedAndRestart() {
513+ loadDesktopShellWithApps();
514+
515+ var appRepeater = findChild(shell, "appRepeater")
516+ var appId = ApplicationManager.get(0).appId;
517+ var appDelegate = appRepeater.itemAt(0);
518+ var maximizeButton = findChild(appDelegate, "maximizeWindowButton");
519+
520+ tryCompare(appDelegate, "state", "normal");
521+ tryCompare(PanelState, "buttonsVisible", false)
522+
523+ mouseClick(maximizeButton, maximizeButton.width / 2, maximizeButton.height / 2);
524+ tryCompare(appDelegate, "state", "maximized");
525+ tryCompare(PanelState, "buttonsVisible", true)
526+
527+ ApplicationManager.stopApplication(appId);
528+ tryCompare(PanelState, "buttonsVisible", false)
529+
530+ ApplicationManager.startApplication(appId);
531+ tryCompare(PanelState, "buttonsVisible", true)
532+ }
533+
534+ function test_newAppHasValidGeometry() {
535+ loadDesktopShellWithApps();
536+ var appRepeater = findChild(shell, "appRepeater");
537+ var appId = ApplicationManager.get(0).appId;
538+ var appDelegate = appRepeater.itemAt(0);
539+
540+ var resizeArea = findChild(appDelegate, "windowResizeArea");
541+ var priv = findInvisibleChild(resizeArea, "priv");
542+
543+ // Make sure windows are at 0,0 or greater and they have a size that's > 0
544+ compare(priv.normalX >= 0, true)
545+ compare(priv.normalY >= 0, true)
546+ compare(priv.normalWidth > 0, true)
547+ compare(priv.normalHeight > 0, true)
548+ }
549+
550+ // bug http://pad.lv/1431566
551+ function test_switchToStagedHidesPanelButtons() {
552+ loadDesktopShellWithApps();
553+ var appRepeater = findChild(shell, "appRepeater")
554+ var appId = ApplicationManager.get(0).appId;
555+ var appDelegate = appRepeater.itemAt(0);
556+ var panelButtons = findChild(shell, "panelWindowControlButtons")
557+
558+ tryCompare(appDelegate, "state", "normal");
559+ tryCompare(panelButtons, "visible", false);
560+
561+ appDelegate.maximize(false);
562+ tryCompare(panelButtons, "visible", true);
563+
564+ shell.usageScenario = "phone";
565+ waitForRendering(shell);
566+ tryCompare(panelButtons, "visible", false);
567+
568+ shell.usageScenario = "desktop";
569+ waitForRendering(shell);
570+ tryCompare(panelButtons, "visible", true);
571+ }
572+
573+ function test_lockingGreeterHidesPanelButtons() {
574+ loadDesktopShellWithApps();
575+ var appRepeater = findChild(shell, "appRepeater")
576+ var appId = ApplicationManager.get(0).appId;
577+ var appDelegate = appRepeater.itemAt(0);
578+ var panelButtons = findChild(shell, "panelWindowControlButtons")
579+
580+ tryCompare(appDelegate, "state", "normal");
581+ tryCompare(panelButtons, "visible", false);
582+
583+ appDelegate.maximize(false);
584+ tryCompare(panelButtons, "visible", true);
585+
586+ LightDM.Greeter.showGreeter();
587+ waitForRendering(shell);
588+ tryCompare(panelButtons, "visible", false);
589+
590+ LightDM.Greeter.hideGreeter();
591+ waitForRendering(shell);
592+ tryCompare(panelButtons, "visible", true);
593+ }
594 }
595 }

Subscribers

People subscribed via source and target branches