on xenial help sign in
Refactor keymap switching code

- Make it declarative - Isolate it in a single file

commit7a0b76573f63e0f5f0351df504cd68f16769182e authorDaniel d'Andrada <daniel.dandrada@canonical.com> dateMon 4 Apr 2016, 10:28 (11 years ago) parentad46288
14 files changed+107−121
CMakeLists.txt+1−1 debian/control+2−2 qml/Components/KeymapSwitcher.qml+73 qml/Shell.qml+2−49 qml/Stages/AbstractStage.qml−1 qml/Stages/ApplicationWindow.qml−4 qml/Stages/DesktopStage.qml−4 qml/Stages/PhoneStage.qml−2 qml/Stages/SpreadDelegate.qml−1 qml/Stages/SurfaceContainer.qml−13 qml/Stages/TabletStage.qml−2 tests/mocks/Unity/Application/MirSurface.cpp+14−20 tests/mocks/Unity/Application/MirSurface.h+6−6 tests/qmltests/tst_Shell.qml+9−16
CMakeLists.txt+1−1
@@ -57,7 +57,7 @@ find_package(Qt5DBus 5.4 REQUIRED)
5757find_package(Qt5Concurrent 5.4 REQUIRED)
5858find_package(Qt5Sql 5.4 REQUIRED)
5959
60−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=14)
60+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=15)
6161pkg_check_modules(GEONAMES REQUIRED geonames>=0.2)
6262pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
6363pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
debian/control+2−2
@@ -133,7 +133,7 @@ Depends: qml-module-qtquick-layouts,
133133 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3.1845) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3.1845),
134134 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
135135 ubuntu-thumbnailer-impl-0,
136− unity-application-impl-14,
136+ unity-application-impl-15,
137137 unity-notifications-impl-3,
138138 unity-plugin-scopes | unity-scopes-impl,
139139 unity-scopes-impl-11,
@@ -179,7 +179,7 @@ Pre-Depends: ${misc:Pre-Depends},
179179Depends: ${misc:Depends},
180180 ${shlibs:Depends},
181181Provides: unity-application-impl,
182− unity-application-impl-14,
182+ unity-application-impl-15,
183183Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1)
184184Description: Fake environment for running Unity 8 shell
185185 Provides fake implementations of some QML modules used by Unity 8 shell
qml/Components/KeymapSwitcher.qmlnew file+73
@@ -0,0 +1,73 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import AccountsService 0.1
19+import GlobalShortcut 1.0
20+import Unity.Application 0.1
21+
22+QtObject {
23+ id: root
24+
25+ property GlobalShortcut shortcutNext: GlobalShortcut {
26+ shortcut: Qt.MetaModifier|Qt.Key_Space
27+ onTriggered: root.nextKeymap()
28+ active: root.keymapCount > 1
29+ }
30+
31+ property GlobalShortcut shortcutPrevious: GlobalShortcut {
32+ shortcut: Qt.MetaModifier|Qt.ShiftModifier|Qt.Key_Space
33+ onTriggered: root.previousKeymap()
34+ active: root.keymapCount > 1
35+ }
36+
37+ readonly property var keymaps: AccountsService.keymaps
38+ readonly property int keymapCount: keymaps.length
39+ property int currentKeymapIndex: 0
40+ readonly property string currentKeymap: keymaps[currentKeymapIndex]
41+
42+ function nextKeymap() {
43+ var nextIndex = 0;
44+
45+ if (currentKeymapIndex !== -1 && currentKeymapIndex < keymapCount - 1) {
46+ nextIndex = currentKeymapIndex + 1;
47+ }
48+ currentKeymapIndex = nextIndex;
49+ }
50+
51+ function previousKeymap() {
52+ var prevIndex = keymapCount - 1;
53+
54+ if (currentKeymapIndex > 0) {
55+ prevIndex = currentKeymapIndex - 1;
56+ }
57+ currentKeymapIndex = prevIndex;
58+ }
59+
60+ // Code below will get much simpler with surface-based window management (the upcoming MirFocusController)
61+ property var application: ApplicationManager.focusedApplicationId
62+ ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
63+ : null
64+ property var session: application && application.session ? application.session : null
65+ property var surface: session ? session.lastSurface : null
66+
67+ property Binding surfaceKeymapBinding: Binding {
68+ target: root.surface
69+ when: root.surface != null && root.surface.live
70+ property: "keymap"
71+ value: root.currentKeymap
72+ }
73+}
qml/Shell.qml+2−49
@@ -102,8 +102,6 @@ Item {
102102 // internal props from here onwards
103103 readonly property var mainApp:
104104 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null
105− readonly property var mainAppWindow:
106− applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindow : null
107105
108106 // Disable everything while greeter is waiting, so that the user can't swipe
109107 // the greeter or launcher until we know whether the session is locked.
@@ -721,53 +719,8 @@ Item {
721719 onMouseMoved: { cursor.opacity = 1; }
722720 }
723721
724− // keymap switching
725− GlobalShortcut {
726− shortcut: Qt.MetaModifier|Qt.Key_Space
727− onTriggered: keymapPriv.nextKeymap()
728− active: keymapPriv.keymapCount > 1
729− }
730−
731− GlobalShortcut {
732− shortcut: Qt.MetaModifier|Qt.ShiftModifier|Qt.Key_Space
733− onTriggered: keymapPriv.previousKeymap()
734− active: keymapPriv.keymapCount > 1
735− }
736−
737− QtObject {
738− id: keymapPriv
739−
740− readonly property var keymaps: AccountsService.keymaps
741− readonly property int keymapCount: keymaps.length
742− property int currentKeymapIndex: 0 // the new one that we're setting
743− onCurrentKeymapIndexChanged: switchToKeymap();
744−
745− function nextKeymap() {
746− var nextIndex = 0;
747−
748− if (currentKeymapIndex !== -1 && currentKeymapIndex < keymapCount - 1) {
749− nextIndex = currentKeymapIndex + 1;
750− }
751− currentKeymapIndex = nextIndex;
752− }
753−
754− function previousKeymap() {
755− var prevIndex = keymapCount - 1;
756−
757− if (currentKeymapIndex > 0) {
758− prevIndex = currentKeymapIndex - 1;
759− }
760− currentKeymapIndex = prevIndex;
761− }
762−
763− function switchToKeymap() {
764− if (mainAppWindow) {
765− mainAppWindow.switchToKeymap(keymaps[currentKeymapIndex]);
766− }
767− }
768− }
769−
770− onMainAppWindowChanged: keymapPriv.switchToKeymap()
722+ // non-visual item
723+ KeymapSwitcher {}
771724
772725 Rectangle {
773726 id: shutdownFadeOutRectangle
qml/Stages/AbstractStage.qml−1
@@ -45,7 +45,6 @@ Rectangle {
4545
4646 // To be read from outside
4747 property var mainApp: null
48− property var mainAppWindow: null
4948 property int mainAppWindowOrientationAngle: 0
5049 property bool orientationChangesEnabled
5150 property int supportedOrientations: Qt.PortraitOrientation
qml/Stages/ApplicationWindow.qml−4
@@ -39,10 +39,6 @@ FocusScope {
3939 property int requestedWidth: -1
4040 property int requestedHeight: -1
4141
42− function switchToKeymap(keymap) {
43− sessionContainer.surfaceContainer.switchToKeymap(keymap);
44− }
45−
4642 readonly property int minimumWidth: sessionContainer.surface ? sessionContainer.surface.minimumWidth : 0
4743 readonly property int minimumHeight: sessionContainer.surface ? sessionContainer.surface.minimumHeight : 0
4844 readonly property int maximumWidth: sessionContainer.surface ? sessionContainer.surface.maximumWidth : 0
qml/Stages/DesktopStage.qml−4
@@ -43,8 +43,6 @@ AbstractStage {
4343 ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
4444 : null
4545
46− mainAppWindow: priv.focusedAppDelegate ? priv.focusedAppDelegate.appWindow : null
47−
4846 // application windows never rotate independently
4947 mainAppWindowOrientationAngle: shellOrientationAngle
5048
@@ -289,8 +287,6 @@ AbstractStage {
289287 property bool visuallyMaximized: false
290288 property bool visuallyMinimized: false
291289
292− readonly property alias appWindow: decoratedWindow.window
293−
294290 onFocusChanged: {
295291 if (focus && ApplicationManager.focusedApplicationId !== appId) {
296292 ApplicationManager.focusApplication(appId);
qml/Stages/PhoneStage.qml−2
@@ -111,8 +111,6 @@ AbstractStage {
111111 ? applicationManager.findApplication(applicationManager.focusedApplicationId)
112112 : null
113113
114− mainAppWindow: priv.focusedAppDelegate ? priv.focusedAppDelegate.appWindow : null
115−
116114 orientationChangesEnabled: priv.focusedAppOrientationChangesEnabled
117115 && !priv.focusedAppDelegateIsDislocated
118116 && !(priv.focusedAppDelegate && priv.focusedAppDelegate.xBehavior.running)
qml/Stages/SpreadDelegate.qml−1
@@ -37,7 +37,6 @@ FocusScope {
3737 | Qt.LandscapeOrientation
3838 | Qt.InvertedPortraitOrientation
3939 | Qt.InvertedLandscapeOrientation
40− readonly property alias appWindow: appWindow
4140
4241 // to be set from outside
4342 property bool interactive: true
qml/Stages/SurfaceContainer.qml−13
@@ -19,7 +19,6 @@ import Ubuntu.Components 1.3
1919import Ubuntu.Gestures 0.1 // For TouchGate
2020import Utils 0.1 // for InputWatcher
2121import Unity.Application 0.1 // for MirSurfaceItem
22−import AccountsService 0.1
2322
2423FocusScope {
2524 id: root
@@ -35,22 +34,10 @@ FocusScope {
3534 property int requestedWidth: -1
3635 property int requestedHeight: -1
3736
38− property string savedKeymap: AccountsService.keymaps[0] // start with the user default
39−
4037 onSurfaceChanged: {
4138 if (surface) {
4239 surfaceItem.surface = surface;
4340 root.hadSurface = false;
44− switchToKeymap(savedKeymap);
45− }
46− }
47−
48− function switchToKeymap(keymap) {
49− var finalKeymap = keymap.split("+");
50− savedKeymap = keymap; // save the keymap in case the surface changes later
51−
52− if (surface) {
53− surface.setKeymap(finalKeymap[0], finalKeymap[1] || "");
5441 }
5542 }
5643
qml/Stages/TabletStage.qml−2
@@ -79,8 +79,6 @@ AbstractStage {
7979 }
8080 }
8181
82− mainAppWindow: priv.focusedAppDelegate ? priv.focusedAppDelegate.appWindow : null
83−
8482 orientationChangesEnabled: priv.mainAppOrientationChangesEnabled
8583
8684 supportedOrientations: {
tests/mocks/Unity/Application/MirSurface.cpp+14−20
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2015 Canonical, Ltd.
2+ * Copyright (C) 2015-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -134,6 +134,19 @@ void MirSurface::setOrientationAngle(Mir::OrientationAngle angle)
134134 Q_EMIT orientationAngleChanged(angle);
135135}
136136
137+void MirSurface::setKeymap(const QString &value)
138+{
139+ if (value != m_keymap) {
140+ m_keymap = value;
141+ Q_EMIT keymapChanged(m_keymap);
142+ }
143+}
144+
145+QString MirSurface::keymap() const
146+{
147+ return m_keymap;
148+}
149+
137150Mir::ShellChrome MirSurface::shellChrome() const
138151{
139152 return m_shellChrome;
@@ -148,25 +161,6 @@ void MirSurface::setShellChrome(Mir::ShellChrome shellChrome)
148161 Q_EMIT shellChromeChanged(shellChrome);
149162}
150163
151−QString MirSurface::keymapLayout() const
152−{
153− return m_keyMap.first;
154−}
155−
156−QString MirSurface::keymapVariant() const
157−{
158− return m_keyMap.second;
159−}
160−
161−void MirSurface::setKeymap(const QString &layout, const QString &variant)
162−{
163− if (layout.isEmpty()) {
164− return;
165− }
166− m_keyMap = qMakePair(layout, variant);
167− Q_EMIT keymapChanged(layout, variant);
168−}
169−
170164void MirSurface::registerView(qintptr viewId)
171165{
172166 m_views.insert(viewId, MirSurface::View{false});
tests/mocks/Unity/Application/MirSurface.h+6−6
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2015 Canonical, Ltd.
2+ * Copyright (C) 2015-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -74,10 +74,10 @@ public:
7474 int widthIncrement() const override { return m_widthIncrement; }
7575 int heightIncrement() const override { return m_heightIncrement; }
7676
77+ virtual void setKeymap(const QString &);
78+ virtual QString keymap() const;
79+
7780 Mir::ShellChrome shellChrome() const override;
78− QString keymapLayout() const override;
79− QString keymapVariant() const override;
80− Q_INVOKABLE void setKeymap(const QString &layout, const QString &variant) override;
8181
8282 ////
8383 // API for tests
@@ -150,6 +150,8 @@ private:
150150 int m_widthIncrement{0};
151151 int m_heightIncrement{0};
152152
153+ QString m_keymap;
154+
153155 bool m_slowToResize;
154156 QTimer m_delayedResizeTimer;
155157 QSize m_delayedResize;
@@ -161,8 +163,6 @@ private:
161163 bool visible;
162164 };
163165 QHash<qintptr, View> m_views;
164−
165− QPair<QString,QString> m_keyMap; // pair of layout+variant
166166};
167167
168168#endif // MOCK_MIR_SURFACE_H
tests/qmltests/tst_Shell.qml+9−16
@@ -2299,16 +2299,15 @@ Rectangle {
22992299 waitUntilAppWindowIsFullyLoaded(app);
23002300
23012301 // verify the initial keymap of the newly started app is the first one from the list
2302− tryCompare(app.session.lastSurface, "keymapLayout", "sk");
2303− tryCompare(app.session.lastSurface, "keymapVariant", "");
2302+ tryCompare(app.session.lastSurface, "keymap", "sk");
23042303
23052304 // switch to next keymap, should go to "cz+qwerty"
23062305 keyClick(Qt.Key_Space, Qt.MetaModifier);
2307− tryCompare(app.session.lastSurface, "keymapLayout", "cz");
2308− tryCompare(app.session.lastSurface, "keymapVariant", "qwerty");
2306+ tryCompare(app.session.lastSurface, "keymap", "cz+qwerty");
23092307
23102308 // switch to next keymap, should go to "fr"
23112309 keyClick(Qt.Key_Space, Qt.MetaModifier);
2310+ tryCompare(app.session.lastSurface, "keymap", "fr");
23122311
23132312 // go to e.g. desktop stage
23142313 loadShell("desktop");
@@ -2318,34 +2317,28 @@ Rectangle {
23182317 // start a second app, should get the last configured keyboard, "fr"
23192318 var app2 = ApplicationManager.startApplication("calendar-app");
23202319 waitUntilAppWindowIsFullyLoaded(app2);
2321− tryCompare(app2.session.lastSurface, "keymapLayout", "fr");
2322− tryCompare(app2.session.lastSurface, "keymapVariant", "");
2320+ tryCompare(app2.session.lastSurface, "keymap", "fr");
23232321
23242322 // focus our first app, make sure it also has the "fr" keymap
23252323 ApplicationManager.requestFocusApplication("dialer-app");
2326− tryCompare(app.session.lastSurface, "keymapLayout", "fr");
2327− tryCompare(app.session.lastSurface, "keymapVariant", "");
2324+ tryCompare(app.session.lastSurface, "keymap", "fr");
23282325
23292326 // switch to previous keymap, should be "cz+qwerty"
23302327 keyClick(Qt.Key_Space, Qt.MetaModifier|Qt.ShiftModifier);
2331− tryCompare(app.session.lastSurface, "keymapLayout", "cz");
2332− tryCompare(app.session.lastSurface, "keymapVariant", "qwerty");
2328+ tryCompare(app.session.lastSurface, "keymap", "cz+qwerty");
23332329
23342330 // go next twice to "sk", past the end
23352331 keyClick(Qt.Key_Space, Qt.MetaModifier);
23362332 keyClick(Qt.Key_Space, Qt.MetaModifier);
2337− tryCompare(app.session.lastSurface, "keymapLayout", "sk");
2338− tryCompare(app.session.lastSurface, "keymapVariant", "");
2333+ tryCompare(app.session.lastSurface, "keymap", "sk");
23392334
23402335 // go back once to past the beginning, to "fr"
23412336 keyClick(Qt.Key_Space, Qt.MetaModifier|Qt.ShiftModifier);
2342− tryCompare(app.session.lastSurface, "keymapLayout", "fr");
2343− tryCompare(app.session.lastSurface, "keymapVariant", "");
2337+ tryCompare(app.session.lastSurface, "keymap", "fr");
23442338
23452339 // switch to app2, should also get "fr"
23462340 ApplicationManager.requestFocusApplication("calendar-app");
2347− tryCompare(app2.session.lastSurface, "keymapLayout", "fr");
2348− tryCompare(app2.session.lastSurface, "keymapVariant", "");
2341+ tryCompare(app2.session.lastSurface, "keymap", "fr");
23492342 }
23502343 }
23512344}