Merge lp:~mzanetti/unity8/panel-button-fixes into lp:unity8
- panel-button-fixes
- Merge into trunk
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 |
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Needs Fixing | |
Lukáš Tinkl (community) | Approve | ||
Michał Sawicz | Pending | ||
Review via email:
|
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

Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal | # |

Michael Zanetti (mzanetti) wrote : Posted in a previous version of this proposal | # |
ok, will redo it on top of that

PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1982
http://
Executed test runs:
SUCCESS: http://
UNSTABLE: http://
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://

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 :)

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.

PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1983
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://

PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1984
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://

PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1986
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://

PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1986
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://

Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal | # |
Text conflict in debian/control
Text conflict in plugins/
Text conflict in qml/OrientedShe
3 conflicts encountered.

Michael Zanetti (mzanetti) wrote : Posted in a previous version of this proposal | # |
> Text conflict in debian/control
> Text conflict in plugins/
> Text conflict in qml/OrientedShe
> 3 conflicts encountered.
They're to be resolved in the prerequisite (mousePointer)

Michał Sawicz (saviq) : Posted in a previous version of this proposal | # |

PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:1987
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://

Albert Astals Cid (aacid) wrote : Posted in a previous version of this proposal | # |
Text conflict in CMakeLists.txt
1 conflicts encountered.

Lukáš Tinkl (lukas-kde) wrote : Posted in a previous version of this proposal | # |
Some inline comments
- 1989. By Michael Zanetti
-
fix some review notes

Michael Zanetti (mzanetti) wrote : | # |
> Some inline comments
fixed

Lukáš Tinkl (lukas-kde) wrote : | # |
Just for the record:
animationsEnabled = (animated === undefined) | animated;
this should use a logical "or", not a bitwise

PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1988
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1990. By Michael Zanetti
-
fix default arg handling

Michael Zanetti (mzanetti) wrote : | # |
> Just for the record:
>
> animationsEnabled = (animated === undefined) | animated;
>
> this should use a logical "or", not a bitwise
oops. fixed, thanks!

PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1989
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://

PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1990
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1991. By Michael Zanetti
-
merge with prereq

PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1991
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://

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).
- 1992. By Michael Zanetti
-
make sure panel buttons are not visible while greeter is shown
- 1993. By Michael Zanetti
-
drop debug print

PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1992
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://

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

PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1993
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1994. By Michael Zanetti
-
merge trunk

PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1993
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
UNSTABLE: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1995. By Michael Zanetti
-
fix tests (clear the mock state store between tests)

PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1995
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1996. By Michael Zanetti
-
fix initialisation of window storage state for new apps/windows

PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1996
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
ABORTED: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
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 | } |
It should conflict BIG TIME with mousePointer as it changes the resize area as well