on xenial help sign in
Merge lp:~mzanetti/unity8/panel-button-fixes
6e4498bMichał Sawicz
plugins/Utils/windowstatestorage.cpp+61−22
@@ -47,26 +47,38 @@ WindowStateStorage::~WindowStateStorage()
47 m_db.close();
47 m_db.close();
48}
48}
49
49
50−void WindowStateStorage::saveGeometry(const QString &windowId, const QRect &rect)
50+void WindowStateStorage::saveState(const QString &windowId, WindowStateStorage::WindowState state)
51{
51{
52− QMutexLocker mutexLocker(&s_mutex);
52+ const QString queryString = QStringLiteral("INSERT OR REPLACE INTO state (windowId, state) values ('%1', '%2');")
53+ .arg(windowId)
54+ .arg((int)state);
55+
56+ saveValue(queryString);
57+}
58+
59+WindowStateStorage::WindowState WindowStateStorage::getState(const QString &windowId, WindowStateStorage::WindowState defaultValue) const
60+{
61+ const QString queryString = QStringLiteral("SELECT * FROM state WHERE windowId = '%1';")
62+ .arg(windowId);
53
63
64+ QSqlQuery query = getValue(queryString);
65+
66+ if (!query.first()) {
67+ return defaultValue;
68+ }
69+ return (WindowState)query.value("state").toInt();
70+}
71+
72+void WindowStateStorage::saveGeometry(const QString &windowId, const QRect &rect)
73+{
54− QString queryString = QStringLiteral("INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values ('%1', '%2', '%3', '%4', '%5');")
74+ const QString queryString = QStringLiteral("INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values ('%1', '%2', '%3', '%4', '%5');")
55 .arg(windowId)
75 .arg(windowId)
56 .arg(rect.x())
76 .arg(rect.x())
57 .arg(rect.y())
77 .arg(rect.y())
58 .arg(rect.width())
78 .arg(rect.width())
59 .arg(rect.height());
79 .arg(rect.height());
60
80
61− QFuture<void> future = QtConcurrent::run(executeAsyncQuery, queryString);
81+ saveValue(queryString);
62− m_asyncQueries.append(future);
63−
64− QFutureWatcher<void> *futureWatcher = new QFutureWatcher<void>();
65− futureWatcher->setFuture(future);
66− connect(futureWatcher, &QFutureWatcher<void>::finished,
67− this,
68− [=](){ m_asyncQueries.removeAll(futureWatcher->future());
69− futureWatcher->deleteLater(); });
70}
82}
71
83
72void WindowStateStorage::executeAsyncQuery(const QString &queryString)
84void WindowStateStorage::executeAsyncQuery(const QString &queryString)
@@ -82,20 +94,13 @@ void WindowStateStorage::executeAsyncQuery(const QString &queryString)
82 }
94 }
83}
95}
84
96
85−QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue)
97+QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue) const
86{
98{
87− QMutexLocker l(&s_mutex);
88 QString queryString = QStringLiteral("SELECT * FROM geometry WHERE windowId = '%1';")
99 QString queryString = QStringLiteral("SELECT * FROM geometry WHERE windowId = '%1';")
89 .arg(windowId);
100 .arg(windowId);
90− QSqlQuery query;
91
101
92− bool ok = query.exec(queryString);
102+ QSqlQuery query = getValue(queryString);
93− if (!ok) {
103+
94− qWarning() << "Error retrieving window state for" << windowId
95− << "Driver error:" << query.lastError().driverText()
96− << "Database error:" << query.lastError().databaseText();
97− return defaultValue;
98− }
99 if (!query.first()) {
104 if (!query.first()) {
100 return defaultValue;
105 return defaultValue;
101 }
106 }
@@ -114,4 +119,38 @@ void WindowStateStorage::initdb()
114 QSqlQuery query;
119 QSqlQuery query;
115 query.exec(QStringLiteral("CREATE TABLE geometry(windowId TEXT UNIQUE, x INTEGER, y INTEGER, width INTEGER, height INTEGER);"));
120 query.exec(QStringLiteral("CREATE TABLE geometry(windowId TEXT UNIQUE, x INTEGER, y INTEGER, width INTEGER, height INTEGER);"));
116 }
121 }
122+
123+ if (!m_db.tables().contains("state")) {
124+ QSqlQuery query;
125+ query.exec("CREATE TABLE state(windowId TEXT UNIQUE, state INTEGER);");
126+ }
127+}
128+
129+void WindowStateStorage::saveValue(const QString &queryString)
130+{
131+ QMutexLocker mutexLocker(&s_mutex);
132+
133+ QFuture<void> future = QtConcurrent::run(executeAsyncQuery, queryString);
134+ m_asyncQueries.append(future);
135+
136+ QFutureWatcher<void> *futureWatcher = new QFutureWatcher<void>();
137+ futureWatcher->setFuture(future);
138+ connect(futureWatcher, &QFutureWatcher<void>::finished,
139+ this,
140+ [=](){ m_asyncQueries.removeAll(futureWatcher->future());
141+ futureWatcher->deleteLater(); });
142+}
143+
144+QSqlQuery WindowStateStorage::getValue(const QString &queryString) const
145+{
146+ QMutexLocker l(&s_mutex);
147+ QSqlQuery query;
148+
149+ bool ok = query.exec(queryString);
150+ if (!ok) {
151+ qWarning() << "Error retrieving database query:" << queryString
152+ << "Driver error:" << query.lastError().driverText()
153+ << "Database error:" << query.lastError().databaseText();
154+ }
155+ return query;
117}
156}
plugins/Utils/windowstatestorage.h+13−1
@@ -22,16 +22,28 @@
22class WindowStateStorage: public QObject
22class WindowStateStorage: public QObject
23{
23{
24 Q_OBJECT
24 Q_OBJECT
25+ Q_ENUMS(WindowState)
25public:
26public:
27+ enum WindowState {
28+ WindowStateNormal,
29+ WindowStateMaximized
30+ };
31+
26 WindowStateStorage(QObject *parent = 0);
32 WindowStateStorage(QObject *parent = 0);
27 virtual ~WindowStateStorage();
33 virtual ~WindowStateStorage();
28
34
35+ Q_INVOKABLE void saveState(const QString &windowId, WindowState state);
36+ Q_INVOKABLE WindowState getState(const QString &windowId, WindowState defaultValue) const;
37+
29 Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
38 Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
30− Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue);
39+ Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue) const;
31
40
32private:
41private:
33 void initdb();
42 void initdb();
34
43
44+ void saveValue(const QString &queryString);
45+ QSqlQuery getValue(const QString &queryString) const;
46+
35 static void executeAsyncQuery(const QString &queryString);
47 static void executeAsyncQuery(const QString &queryString);
36 static QMutex s_mutex;
48 static QMutex s_mutex;
37
49
qml/Components/WindowControlButtons.qml+3
@@ -28,6 +28,7 @@ Row {
28 signal maximize()
28 signal maximize()
29
29
30 Rectangle {
30 Rectangle {
31+ objectName: "closeWindowButton"
31 height: parent.height; width: height; radius: height / 2
32 height: parent.height; width: height; radius: height / 2
32 gradient: Gradient {
33 gradient: Gradient {
33 GradientStop { color: "#F49073"; position: 0 }
34 GradientStop { color: "#F49073"; position: 0 }
@@ -38,6 +39,7 @@ Row {
38 MouseArea { anchors.fill: parent; onClicked: root.close() }
39 MouseArea { anchors.fill: parent; onClicked: root.close() }
39 }
40 }
40 Rectangle {
41 Rectangle {
42+ objectName: "minimizeWindowButton"
41 height: parent.height; width: height; radius: height / 2
43 height: parent.height; width: height; radius: height / 2
42 gradient: Gradient {
44 gradient: Gradient {
43 GradientStop { color: "#92918C"; position: 0 }
45 GradientStop { color: "#92918C"; position: 0 }
@@ -48,6 +50,7 @@ Row {
48 MouseArea { anchors.fill: parent; onClicked: root.minimize() }
50 MouseArea { anchors.fill: parent; onClicked: root.minimize() }
49 }
51 }
50 Rectangle {
52 Rectangle {
53+ objectName: "maximizeWindowButton"
51 height: parent.height; width: height; radius: height / 2
54 height: parent.height; width: height; radius: height / 2
52 gradient: Gradient {
55 gradient: Gradient {
53 GradientStop { color: "#92918C"; position: 0 }
56 GradientStop { color: "#92918C"; position: 0 }
qml/Panel/Panel.qml+5−3
@@ -28,6 +28,7 @@ Item {
28 property alias callHint: __callHint
28 property alias callHint: __callHint
29 property bool fullscreenMode: false
29 property bool fullscreenMode: false
30 property real indicatorAreaShowProgress: 1.0
30 property real indicatorAreaShowProgress: 1.0
31+ property bool locked: false
31
32
32 opacity: fullscreenMode && indicators.fullyClosed ? 0.0 : 1.0
33 opacity: fullscreenMode && indicators.fullyClosed ? 0.0 : 1.0
33
34
@@ -136,7 +137,7 @@ Item {
136 }
137 }
137
138
138 shown: false
139 shown: false
139− width: root.width - (PanelState.buttonsVisible ? windowControlButtons.width : 0)
140+ width: root.width - (windowControlButtons.visible ? windowControlButtons.width : 0)
140 minimizedPanelHeight: units.gu(3)
141 minimizedPanelHeight: units.gu(3)
141 expandedPanelHeight: units.gu(7)
142 expandedPanelHeight: units.gu(7)
142 openedHeight: root.height - indicatorOrangeLine.height
143 openedHeight: root.height - indicatorOrangeLine.height
@@ -164,13 +165,14 @@ Item {
164
165
165 WindowControlButtons {
166 WindowControlButtons {
166 id: windowControlButtons
167 id: windowControlButtons
168+ objectName: "panelWindowControlButtons"
167 anchors {
169 anchors {
168 left: parent.left
170 left: parent.left
169 top: parent.top
171 top: parent.top
170 margins: units.gu(0.7)
172 margins: units.gu(0.7)
171 }
173 }
172 height: indicators.minimizedPanelHeight - anchors.margins * 2
174 height: indicators.minimizedPanelHeight - anchors.margins * 2
173− visible: PanelState.buttonsVisible
175+ visible: PanelState.buttonsVisible && !root.locked
174 onClose: PanelState.close()
176 onClose: PanelState.close()
175 onMinimize: PanelState.minimize()
177 onMinimize: PanelState.minimize()
176 onMaximize: PanelState.maximize()
178 onMaximize: PanelState.maximize()
@@ -189,7 +191,7 @@ Item {
189 id: __callHint
191 id: __callHint
190 anchors {
192 anchors {
191 top: parent.top
193 top: parent.top
192− left: PanelState.buttonsVisible ? windowControlButtons.right : parent.left
194+ left: windowControlButtons.visible ? windowControlButtons.right : parent.left
193 }
195 }
194 height: indicators.minimizedPanelHeight
196 height: indicators.minimizedPanelHeight
195 visible: active && indicators.state == "initial"
197 visible: active && indicators.state == "initial"
qml/Shell.qml+2
@@ -56,6 +56,7 @@ Item {
56 property bool beingResized
56 property bool beingResized
57 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
57 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
58 property string mode: "full-greeter"
58 property string mode: "full-greeter"
59+ property bool cursorVisible: false
59 function updateFocusedAppOrientation() {
60 function updateFocusedAppOrientation() {
60 applicationsDisplayLoader.item.updateFocusedAppOrientation();
61 applicationsDisplayLoader.item.updateFocusedAppOrientation();
61 }
62 }
@@ -530,6 +531,7 @@ Item {
530
531
531 fullscreenMode: (topmostApplicationIsFullscreen && !lightDM.greeter.active && launcher.progress == 0)
532 fullscreenMode: (topmostApplicationIsFullscreen && !lightDM.greeter.active && launcher.progress == 0)
532 || greeter.hasLockedApp
533 || greeter.hasLockedApp
534+ locked: greeter && greeter.active
533 }
535 }
534
536
535 Launcher {
537 Launcher {
qml/Stages/DesktopStage.qml+9−3
@@ -111,6 +111,7 @@ Rectangle {
111 property: "buttonsVisible"
111 property: "buttonsVisible"
112 value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.state === "maximized"
112 value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.state === "maximized"
113 }
113 }
114+ Component.onDestruction: PanelState.buttonsVisible = false;
114
115
115 FocusScope {
116 FocusScope {
116 id: appContainer
117 id: appContainer
@@ -140,6 +141,7 @@ Rectangle {
140
141
141 property bool maximized: false
142 property bool maximized: false
142 property bool minimized: false
143 property bool minimized: false
144+ property bool animationsEnabled: true
143
145
144 onFocusChanged: {
146 onFocusChanged: {
145 if (focus && ApplicationManager.focusedApplicationId !== model.appId) {
147 if (focus && ApplicationManager.focusedApplicationId !== model.appId) {
@@ -158,15 +160,18 @@ Rectangle {
158 value: ApplicationInfoInterface.RequestedRunning // Always running for now
160 value: ApplicationInfoInterface.RequestedRunning // Always running for now
159 }
161 }
160
162
161− function maximize() {
163+ function maximize(animated) {
164+ animationsEnabled = (animated === undefined) || animated;
162 minimized = false;
165 minimized = false;
163 maximized = true;
166 maximized = true;
164 }
167 }
165− function minimize() {
168+ function minimize(animated) {
169+ animationsEnabled = (animated === undefined) || animated;
166 maximized = false;
170 maximized = false;
167 minimized = true;
171 minimized = true;
168 }
172 }
169− function unmaximize() {
173+ function unmaximize(animated) {
174+ animationsEnabled = (animated === undefined) || animated;
170 minimized = false;
175 minimized = false;
171 maximized = false;
176 maximized = false;
172 }
177 }
@@ -188,6 +193,7 @@ Rectangle {
188 Transition {
193 Transition {
189 from: "maximized,minimized,normal,"
194 from: "maximized,minimized,normal,"
190 to: "maximized,minimized,normal,"
195 to: "maximized,minimized,normal,"
196+ enabled: appDelegate.animationsEnabled
191 PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
197 PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
192 },
198 },
193 Transition {
199 Transition {
qml/Stages/WindowResizeArea.qml+38−7
@@ -36,18 +36,49 @@ MouseArea {
36 property int minWidth: 0
36 property int minWidth: 0
37 property int minHeight: 0
37 property int minHeight: 0
38
38
39+ QtObject {
40+ id: priv
41+
42+ property int normalX: 0
43+ property int normalY: 0
44+ property int normalWidth: 0
45+ property int normalHeight: 0
46+
47+ function updateNormalGeometry() {
48+ if (root.target.state == "normal") {
49+ normalX = root.target.x
50+ normalY = root.target.y
51+ normalWidth = root.target.width
52+ normalHeight = root.target.height
53+ }
54+ }
55+ }
56+
57+ Connections {
58+ target: root.target
59+ onXChanged: priv.updateNormalGeometry();
60+ onYChanged: priv.updateNormalGeometry();
61+ onWidthChanged: priv.updateNormalGeometry();
62+ onHeightChanged: priv.updateNormalGeometry();
63+ }
64+
39 Component.onCompleted: {
65 Component.onCompleted: {
40− var windowState = windowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
66+ var windowGeometry = windowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
41− if (windowState !== undefined) {
67+ if (windowGeometry !== undefined) {
42− target.x = windowState.x
68+ target.x = windowGeometry.x
43− target.y = windowState.y
69+ target.y = windowGeometry.y
44− target.width = windowState.width
70+ target.width = windowGeometry.width
45− target.height = windowState.height
71+ target.height = windowGeometry.height
72+ }
73+ var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
74+ if (windowState === WindowStateStorage.WindowStateMaximized) {
75+ target.maximize(false)
46 }
76 }
47 }
77 }
48
78
49 Component.onDestruction: {
79 Component.onDestruction: {
50− windowStateStorage.saveGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
80+ windowStateStorage.saveState(root.windowId, target.state == "maximized" ? WindowStateStorage.WindowStateMaximized : WindowStateStorage.WindowStateNormal)
81+ windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight))
51 }
82 }
52
83
53 QtObject {
84 QtObject {
tests/mocks/Utils/windowstatestorage.cpp+11
@@ -44,3 +44,14 @@ QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defa
44 if (!m_geometry.contains(windowId)) return defaultValue;
44 if (!m_geometry.contains(windowId)) return defaultValue;
45 return m_geometry.value(windowId).toRect();
45 return m_geometry.value(windowId).toRect();
46}
46}
47+
48+void WindowStateStorage::saveState(const QString &windowId, WindowState state)
49+{
50+ m_state[windowId] = state;
51+}
52+
53+WindowStateStorage::WindowState WindowStateStorage::getState(const QString &windowId, WindowStateStorage::WindowState defaultValue)
54+{
55+ if (!m_state.contains(windowId)) return defaultValue;
56+ return m_state.value(windowId);
57+}
tests/mocks/Utils/windowstatestorage.h+9
@@ -22,9 +22,17 @@ class WindowStateStorage: public QObject
22{
22{
23 Q_OBJECT
23 Q_OBJECT
24 Q_PROPERTY(QVariantMap geometry READ geometry WRITE setGeometry NOTIFY geometryChanged)
24 Q_PROPERTY(QVariantMap geometry READ geometry WRITE setGeometry NOTIFY geometryChanged)
25+ Q_ENUMS(WindowState)
25public:
26public:
27+ enum WindowState {
28+ WindowStateNormal,
29+ WindowStateMaximized
30+ };
26 WindowStateStorage(QObject *parent = 0);
31 WindowStateStorage(QObject *parent = 0);
27
32
33+ Q_INVOKABLE void saveState(const QString &windowId, WindowState state);
34+ Q_INVOKABLE WindowState getState(const QString &windowId, WindowState defaultValue);
35+
28 Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
36 Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
29 Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue);
37 Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue);
30
38
@@ -35,5 +43,6 @@ private:
35 void setGeometry(const QVariantMap& geometry);
43 void setGeometry(const QVariantMap& geometry);
36 QVariantMap geometry() const;
44 QVariantMap geometry() const;
37
45
46+ QHash<QString, WindowState> m_state;
38 QVariantMap m_geometry;
47 QVariantMap m_geometry;
39};
48};
tests/qmltests/Stages/tst_WindowResizeArea.qml+35−2
@@ -44,8 +44,11 @@ Item {
44 width: units.gu(20)
44 width: units.gu(20)
45 property int windowHeight: height
45 property int windowHeight: height
46 property int windowWidth: width
46 property int windowWidth: width
47− onWindowHeightChanged: height = windowHeight
47+ state: "normal"
48− onWindowWidthChanged: width = windowWidth
48+
49+ function maximize() {
50+ state = "maximized"
51+ }
49
52
50 WindowResizeArea {
53 WindowResizeArea {
51 id: windowResizeArea
54 id: windowResizeArea
@@ -195,5 +198,35 @@ Item {
195 tryCompare(fakeWindow, "width", initialWindowWidth);
198 tryCompare(fakeWindow, "width", initialWindowWidth);
196 tryCompare(fakeWindow, "height", initialWindowHeight);
199 tryCompare(fakeWindow, "height", initialWindowHeight);
197 }
200 }
201+
202+ function test_saveRestoreMaximized() {
203+ var initialWindowX = fakeWindow.x;
204+ var initialWindowY = fakeWindow.y;
205+
206+ var moveDelta = units.gu(5);
207+
208+ fakeWindow.x = initialWindowX + moveDelta
209+ fakeWindow.y = initialWindowY + moveDelta
210+
211+ // Now change the state to maximized. The window should not keep updating the stored values
212+ fakeWindow.state = "maximized"
213+ fakeWindow.x = 31415 // 0 is too risky to pass the test even when broken
214+ fakeWindow.y = 31415
215+
216+ // This will destroy the window and recreate it
217+ windowLoader.active = false;
218+ waitForRendering(root);
219+ windowLoader.active = true;
220+
221+ // Make sure it's again where we left it in normal state before destroying
222+ tryCompare(fakeWindow, "x", initialWindowX + moveDelta)
223+ tryCompare(fakeWindow, "y", initialWindowX + moveDelta)
224+
225+ // Make sure maximize() has been called after restoring
226+ tryCompare(fakeWindow, "state", "maximized")
227+
228+ // clean up
229+ fakeWindow.state = "normal"
230+ }
198 }
231 }
199}
232}
tests/qmltests/tst_Shell.qml+69
@@ -32,6 +32,7 @@ import Wizard 0.1 as Wizard
32
32
33import "../../qml"
33import "../../qml"
34import "../../qml/Components"
34import "../../qml/Components"
35+import "../../qml/Components/PanelState"
35import "Stages"
36import "Stages"
36
37
37Rectangle {
38Rectangle {
@@ -1707,5 +1708,73 @@ Rectangle {
1707
1708
1708 keyRelease(Qt.Key_Control);
1709 keyRelease(Qt.Key_Control);
1709 }
1710 }
1711+
1712+ // regression test for http://pad.lv/1443319
1713+ function test_closeMaximizedAndRestart() {
1714+ loadDesktopShellWithApps();
1715+
1716+ var appRepeater = findChild(shell, "appRepeater")
1717+ var appId = ApplicationManager.get(0).appId;
1718+ var appDelegate = appRepeater.itemAt(0);
1719+ var maximizeButton = findChild(appDelegate, "maximizeWindowButton");
1720+
1721+ tryCompare(appDelegate, "state", "normal");
1722+ tryCompare(PanelState, "buttonsVisible", false)
1723+
1724+ mouseClick(maximizeButton, maximizeButton.width / 2, maximizeButton.height / 2);
1725+ tryCompare(appDelegate, "state", "maximized");
1726+ tryCompare(PanelState, "buttonsVisible", true)
1727+
1728+ ApplicationManager.stopApplication(appId);
1729+ tryCompare(PanelState, "buttonsVisible", false)
1730+
1731+ ApplicationManager.startApplication(appId);
1732+ tryCompare(PanelState, "buttonsVisible", true)
1733+ }
1734+
1735+ // bug http://pad.lv/1431566
1736+ function test_switchToStagedHidesPanelButtons() {
1737+ loadDesktopShellWithApps();
1738+ var appRepeater = findChild(shell, "appRepeater")
1739+ var appId = ApplicationManager.get(0).appId;
1740+ var appDelegate = appRepeater.itemAt(0);
1741+ var panelButtons = findChild(shell, "panelWindowControlButtons")
1742+
1743+ tryCompare(appDelegate, "state", "normal");
1744+ tryCompare(panelButtons, "visible", false);
1745+
1746+ appDelegate.maximize(false);
1747+ tryCompare(panelButtons, "visible", true);
1748+
1749+ shell.usageScenario = "phone";
1750+ waitForRendering(shell);
1751+ tryCompare(panelButtons, "visible", false);
1752+
1753+ shell.usageScenario = "desktop";
1754+ waitForRendering(shell);
1755+ tryCompare(panelButtons, "visible", true);
1756+ }
1757+
1758+ function test_lockingGreeterHidesPanelButtons() {
1759+ loadDesktopShellWithApps();
1760+ var appRepeater = findChild(shell, "appRepeater")
1761+ var appId = ApplicationManager.get(0).appId;
1762+ var appDelegate = appRepeater.itemAt(0);
1763+ var panelButtons = findChild(shell, "panelWindowControlButtons")
1764+
1765+ tryCompare(appDelegate, "state", "normal");
1766+ tryCompare(panelButtons, "visible", false);
1767+
1768+ appDelegate.maximize(false);
1769+ tryCompare(panelButtons, "visible", true);
1770+
1771+ LightDM.Greeter.showGreeter();
1772+ waitForRendering(shell);
1773+ tryCompare(panelButtons, "visible", false);
1774+
1775+ LightDM.Greeter.hideGreeter();
1776+ waitForRendering(shell);
1777+ tryCompare(panelButtons, "visible", true);
1778+ }
1710 }
1779 }
1711}
1780}