on xenial help sign in
save/restore state of windows too, not just geometry
commit8930f96f6e66832679914ee51c8758afca223709 authorMichael Zanetti <michael.zanetti@canonical.com> dateFri 25 Sep 2015, 19:03 (11 years ago) parentf5c5806
8 files changed+159−31
plugins/Utils/windowstatestorage.cpp+59−20 plugins/Utils/windowstatestorage.h+12 qml/Components/WindowControlButtons.qml+3 qml/Stages/DesktopStage.qml+8−3 qml/Stages/WindowMoveResizeArea.qml+33−8 tests/mocks/Utils/windowstatestorage.cpp+11 tests/mocks/Utils/windowstatestorage.h+9 tests/qmltests/tst_Shell.qml+24
plugins/Utils/windowstatestorage.cpp+59−20
@@ -47,10 +47,30 @@ WindowStateStorage::~WindowStateStorage()
4747 m_db.close();
4848}
4949
50−void WindowStateStorage::saveGeometry(const QString &windowId, const QRect &rect)
50+void WindowStateStorage::saveState(const QString &windowId, WindowStateStorage::WindowState state)
5151{
52− QMutexLocker mutexLocker(&s_mutex);
52+ QString queryString = QString("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)
60+{
61+ QString queryString = QString("SELECT * FROM state WHERE windowId = '%1';")
62+ .arg(windowId);
5363
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+{
5474 QString queryString = QString("INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values ('%1', '%2', '%3', '%4', '%5');")
5575 .arg(windowId)
5676 .arg(rect.x())
@@ -58,15 +78,7 @@ void WindowStateStorage::saveGeometry(const QString &windowId, const QRect &rect
5878 .arg(rect.width())
5979 .arg(rect.height());
6080
61− QFuture<void> future = QtConcurrent::run(executeAsyncQuery, 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(); });
81+ saveValue(queryString);
7082}
7183
7284void WindowStateStorage::executeAsyncQuery(const QString &queryString)
@@ -84,18 +96,11 @@ void WindowStateStorage::executeAsyncQuery(const QString &queryString)
8496
8597QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue)
8698{
87− QMutexLocker l(&s_mutex);
8899 QString queryString = QString("SELECT * FROM geometry WHERE windowId = '%1';")
89100 .arg(windowId);
90− QSqlQuery query;
91101
92− bool ok = query.exec(queryString);
93− if (!ok) {
94− qWarning() << "Error retrieving window state for" << windowId
95− << "Driver error:" << query.lastError().driverText()
96− << "Database error:" << query.lastError().databaseText();
97− return defaultValue;
98− }
102+ QSqlQuery query = getValue(queryString);
103+
99104 if (!query.first()) {
100105 return defaultValue;
101106 }
@@ -114,4 +119,38 @@ void WindowStateStorage::initdb()
114119 QSqlQuery query;
115120 query.exec("CREATE TABLE geometry(windowId TEXT UNIQUE, x INTEGER, y INTEGER, width INTEGER, height INTEGER);");
116121 }
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)
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;
117156}
plugins/Utils/windowstatestorage.h+12
@@ -22,16 +22,28 @@
2222class WindowStateStorage: public QObject
2323{
2424 Q_OBJECT
25+ Q_ENUMS(WindowState)
2526public:
27+ enum WindowState {
28+ WindowStateNormal,
29+ WindowStateMaximized
30+ };
31+
2632 WindowStateStorage(QObject *parent = 0);
2733 virtual ~WindowStateStorage();
2834
35+ Q_INVOKABLE void saveState(const QString &windowId, WindowState state);
36+ Q_INVOKABLE WindowState getState(const QString &windowId, WindowState defaultValue);
37+
2938 Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
3039 Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue);
3140
3241private:
3342 void initdb();
3443
44+ void saveValue(const QString &queryString);
45+ QSqlQuery getValue(const QString &queryString);
46+
3547 static void executeAsyncQuery(const QString &queryString);
3648 static QMutex s_mutex;
3749
qml/Components/WindowControlButtons.qml+3
@@ -28,6 +28,7 @@ Row {
2828 signal maximize()
2929
3030 Rectangle {
31+ objectName: "closeWindowButton"
3132 height: parent.height; width: height; radius: height / 2
3233 gradient: Gradient {
3334 GradientStop { color: "#F49073"; position: 0 }
@@ -38,6 +39,7 @@ Row {
3839 MouseArea { anchors.fill: parent; onClicked: root.close() }
3940 }
4041 Rectangle {
42+ objectName: "minimizeWindowButton"
4143 height: parent.height; width: height; radius: height / 2
4244 gradient: Gradient {
4345 GradientStop { color: "#92918C"; position: 0 }
@@ -48,6 +50,7 @@ Row {
4850 MouseArea { anchors.fill: parent; onClicked: root.minimize() }
4951 }
5052 Rectangle {
53+ objectName: "maximizeWindowButton"
5154 height: parent.height; width: height; radius: height / 2
5255 gradient: Gradient {
5356 GradientStop { color: "#92918C"; position: 0 }
qml/Stages/DesktopStage.qml+8−3
@@ -143,6 +143,7 @@ Rectangle {
143143
144144 property bool maximized: false
145145 property bool minimized: false
146+ property bool animationsEnabled: true
146147
147148 onFocusChanged: {
148149 if (focus && ApplicationManager.focusedApplicationId !== model.appId) {
@@ -161,15 +162,18 @@ Rectangle {
161162 value: ApplicationInfoInterface.RequestedRunning // Always running for now
162163 }
163164
164− function maximize() {
165+ function maximize(animated) {
166+ animationsEnabled = (animated === undefined) | animated;
165167 minimized = false;
166168 maximized = true;
167169 }
168− function minimize() {
170+ function minimize(animated) {
171+ animationsEnabled = (animated === undefined) | animated;
169172 maximized = false;
170173 minimized = true;
171174 }
172− function unmaximize() {
175+ function unmaximize(animated) {
176+ animationsEnabled = (animated === undefined) | animated;
173177 minimized = false;
174178 maximized = false;
175179 }
@@ -191,6 +195,7 @@ Rectangle {
191195 Transition {
192196 from: "maximized,minimized,normal,"
193197 to: "maximized,minimized,normal,"
198+ enabled: appDelegate.animationsEnabled
194199 PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
195200 },
196201 Transition {
qml/Stages/WindowMoveResizeArea.qml+33−8
@@ -47,15 +47,40 @@ MouseArea {
4747 property bool resizeLeft: false
4848 property bool resizeRight: false
4949
50+ property int normalX: 0
51+ property int normalY: 0
52+ property int normalWidth: 0
53+ property int normalHeight: 0
54+
55+ function updateNormalGeometry() {
56+ if (root.target.state == "normal") {
57+ normalX = root.target.x
58+ normalY = root.target.y
59+ normalWidth = root.target.width
60+ normalHeight = root.target.height
61+ }
62+ }
63+ }
64+
65+ Connections {
66+ target: root.target
67+ onXChanged: priv.updateNormalGeometry();
68+ onYChanged: priv.updateNormalGeometry();
69+ onWidthChanged: priv.updateNormalGeometry();
70+ onHeightChanged: priv.updateNormalGeometry();
5071 }
5172
5273 Component.onCompleted: {
53− var windowState = windowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
54− if (windowState !== undefined) {
55− target.x = windowState.x
56− target.y = windowState.y
57− target.width = windowState.width
58− target.height = windowState.height
74+ var windowGeometry = windowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
75+ if (windowGeometry !== undefined) {
76+ target.x = windowGeometry.x
77+ target.y = windowGeometry.y
78+ target.width = windowGeometry.width
79+ target.height = windowGeometry.height
80+ }
81+ var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
82+ if (windowState === WindowStateStorage.WindowStateMaximized) {
83+ target.maximize(false)
5984 }
6085 }
6186
@@ -100,10 +125,10 @@ MouseArea {
100125 target.x += mouseDiff.x;
101126 target.y += mouseDiff.y;
102127 }
103−
104128 }
105129
106130 Component.onDestruction: {
107− windowStateStorage.saveGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
131+ windowStateStorage.saveState(root.windowId, target.state == "maximized" ? WindowStateStorage.WindowStateMaximized : WindowStateStorage.WindowStateNormal)
132+ windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight))
108133 }
109134}
tests/mocks/Utils/windowstatestorage.cpp+11
@@ -44,3 +44,14 @@ QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defa
4444 if (!m_geometry.contains(windowId)) return defaultValue;
4545 return m_geometry.value(windowId).toRect();
4646}
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
2222{
2323 Q_OBJECT
2424 Q_PROPERTY(QVariantMap geometry READ geometry WRITE setGeometry NOTIFY geometryChanged)
25+ Q_ENUMS(WindowState)
2526public:
27+ enum WindowState {
28+ WindowStateNormal,
29+ WindowStateMaximized
30+ };
2631 WindowStateStorage(QObject *parent = 0);
2732
33+ Q_INVOKABLE void saveState(const QString &windowId, WindowState state);
34+ Q_INVOKABLE WindowState getState(const QString &windowId, WindowState defaultValue);
35+
2836 Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
2937 Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue);
3038
@@ -35,5 +43,6 @@ private:
3543 void setGeometry(const QVariantMap& geometry);
3644 QVariantMap geometry() const;
3745
46+ QHash<QString, WindowState> m_state;
3847 QVariantMap m_geometry;
3948};
tests/qmltests/tst_Shell.qml+24
@@ -31,6 +31,7 @@ import Powerd 0.1
3131import Wizard 0.1 as Wizard
3232
3333import "../../qml"
34+import "../../qml/Components/PanelState"
3435import "Stages"
3536
3637Rectangle {
@@ -1731,5 +1732,28 @@ Rectangle {
17311732
17321733 keyRelease(Qt.Key_Control);
17331734 }
1735+
1736+ // regression test for http://pad.lv/1443319
1737+ function test_closeMaximizedAndRestart() {
1738+ loadDesktopShellWithApps();
1739+
1740+ var appRepeater = findChild(shell, "appRepeater")
1741+ var appId = ApplicationManager.get(0).appId;
1742+ var appDelegate = appRepeater.itemAt(0);
1743+ var maximizeButton = findChild(appDelegate, "maximizeWindowButton");
1744+
1745+ tryCompare(appDelegate, "state", "normal");
1746+ tryCompare(PanelState, "buttonsVisible", false)
1747+
1748+ mouseClick(maximizeButton, maximizeButton.width / 2, maximizeButton.height / 2);
1749+ tryCompare(appDelegate, "state", "maximized");
1750+ tryCompare(PanelState, "buttonsVisible", true)
1751+
1752+ ApplicationManager.stopApplication(appId);
1753+ tryCompare(PanelState, "buttonsVisible", false)
1754+
1755+ ApplicationManager.startApplication(appId);
1756+ tryCompare(PanelState, "buttonsVisible", true)
1757+ }
17341758 }
17351759}