Merge lp:~unity-team/unity8/keymapSwitching into lp:unity8

Proposed by Michał Sawicz
Status: Superseded
Proposed branch: lp:~unity-team/unity8/keymapSwitching
Merge into: lp:unity8
Prerequisite: lp:~ci-train-bot/unity8/unity8-ubuntu-xenial-landing-064
Diff against target: 682 lines (+264/-13)
21 files modified
CMakeLists.txt (+1/-1)
debian/control (+3/-3)
plugins/AccountsService/AccountsService.cpp (+26/-0)
plugins/AccountsService/AccountsService.h (+6/-0)
qml/Shell.qml (+53/-0)
qml/Stages/AbstractStage.qml (+1/-0)
qml/Stages/ApplicationWindow.qml (+5/-0)
qml/Stages/DesktopStage.qml (+4/-0)
qml/Stages/PhoneStage.qml (+2/-0)
qml/Stages/SpreadDelegate.qml (+1/-0)
qml/Stages/SurfaceContainer.qml (+16/-0)
qml/Stages/TabletStage.qml (+2/-0)
tests/mocks/AccountsService/AccountsService.cpp (+17/-0)
tests/mocks/AccountsService/AccountsService.h (+8/-0)
tests/mocks/QMenuModel/QDBusActionGroup.qml (+5/-5)
tests/mocks/Unity/Application/MirSurface.cpp (+18/-1)
tests/mocks/Unity/Application/MirSurface.h (+6/-0)
tests/plugins/AccountsService/PropertiesServer.cpp (+9/-0)
tests/plugins/AccountsService/PropertiesServer.h (+0/-1)
tests/plugins/AccountsService/client.cpp (+30/-0)
tests/qmltests/Stages/tst_DesktopStage.qml (+51/-2)
To merge this branch: bzr merge lp:~unity-team/unity8/keymapSwitching
Reviewer Review Type Date Requested Status
Unity8 CI Bot continuous-integration Needs Fixing
Michael Terry Needs Fixing
Review via email: mp+288764@code.launchpad.net

This proposal has been superseded by a proposal from 2016-03-11.

Commit message

Keymap switching support

Description of the change

 * Are there any related MPs required for this MP to build/function as expected? Please list.
    lp:~unity-team/unity-api/kbdLayout
    lp:~unity-team/qtmir/kbdLayout
    lp:~lukas-kde/qtubuntu/kbdLayout

 * Did you perform an exploratory manual test run of your code change and any related functionality?
Will in silo
 * Did you make sure that your branch does not contain spurious tags?
Y
 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Y
 * 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
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Terry (mterry) wrote :

Some inline comments. Haven't reviewed test code yet. Am still setting up my phone to test these branches there.

review: Needs Fixing
2276. By Lukáš Tinkl

drop unneeded changes

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
2277. By Lukáš Tinkl

move the keymap switching to Shell

to survive Stage switching

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
2278. By Michał Sawicz

Merge lp:~unity-team/unity8/shell_chrome

2279. By Lukáš Tinkl

refine test for switching keymaps, move to tst_Shell

2280. By Lukáš Tinkl

remove activeKeymapIndex, refine tests

2281. By Lukáš Tinkl

remove debug

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2016-03-11 17:34:24 +0000
3+++ CMakeLists.txt 2016-03-11 17:34:24 +0000
4@@ -57,7 +57,7 @@
5 find_package(Qt5Concurrent 5.4 REQUIRED)
6 find_package(Qt5Sql 5.4 REQUIRED)
7
8-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=13)
9+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=14)
10 pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
11 pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
12 pkg_check_modules(QMENUMODEL REQUIRED qmenumodel)
13
14=== modified file 'debian/control'
15--- debian/control 2016-03-11 17:34:24 +0000
16+++ debian/control 2016-03-11 17:34:24 +0000
17@@ -29,7 +29,7 @@
18 libqt5xmlpatterns5-dev,
19 libsystemsettings-dev,
20 libudev-dev,
21- libunity-api-dev (>= 7.107),
22+ libunity-api-dev (>= 7.108),
23 libusermetricsoutput1-dev,
24 # Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
25 libx11-dev[!armhf],
26@@ -130,7 +130,7 @@
27 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3.1845) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3.1845),
28 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
29 ubuntu-thumbnailer-impl-0,
30- unity-application-impl-13,
31+ unity-application-impl-14,
32 unity-notifications-impl-3,
33 unity-plugin-scopes | unity-scopes-impl,
34 unity-scopes-impl-10,
35@@ -176,7 +176,7 @@
36 Depends: ${misc:Depends},
37 ${shlibs:Depends},
38 Provides: unity-application-impl,
39- unity-application-impl-13,
40+ unity-application-impl-14,
41 Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1)
42 Description: Fake environment for running Unity 8 shell
43 Provides fake implementations of some QML modules used by Unity 8 shell
44
45=== modified file 'plugins/AccountsService/AccountsService.cpp'
46--- plugins/AccountsService/AccountsService.cpp 2016-03-11 17:34:24 +0000
47+++ plugins/AccountsService/AccountsService.cpp 2016-03-11 17:34:24 +0000
48@@ -37,6 +37,7 @@
49 #define PROP_ENABLE_INDICATORS_WHILE_LOCKED QStringLiteral("EnableIndicatorsWhileLocked")
50 #define PROP_ENABLE_LAUNCHER_WHILE_LOCKED QStringLiteral("EnableLauncherWhileLocked")
51 #define PROP_FAILED_LOGINS QStringLiteral("FailedLogins")
52+#define PROP_INPUT_SOURCES QStringLiteral("InputSources")
53 #define PROP_LICENSE_ACCEPTED QStringLiteral("LicenseAccepted")
54 #define PROP_LICENSE_BASE_PATH QStringLiteral("LicenseBasePath")
55 #define PROP_MOUSE_CURSOR_SPEED QStringLiteral("MouseCursorSpeed")
56@@ -54,6 +55,10 @@
57 #define PROP_TOUCHPAD_TAP_TO_CLICK QStringLiteral("TouchpadTapToClick")
58 #define PROP_TOUCHPAD_TWO_FINGER_SCROLL QStringLiteral("TouchpadTwoFingerScroll")
59
60+using StringMap = QMap<QString,QString>;
61+using StringMapList = QList<StringMap>;
62+Q_DECLARE_METATYPE(StringMapList)
63+
64
65 QVariant primaryButtonConverter(const QVariant &value)
66 {
67@@ -80,6 +85,7 @@
68 connect(m_service, &AccountsServiceDBusAdaptor::maybeChanged, this, &AccountsService::onMaybeChanged);
69
70 registerProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE, QStringLiteral("backgroundFileChanged"));
71+ registerProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES, QStringLiteral("keymapsChanged"));
72 registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, QStringLiteral("hereEnabledChanged"));
73 registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH, QStringLiteral("hereLicensePathChanged"));
74 registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED, QStringLiteral("enableLauncherWhileLockedChanged"));
75@@ -206,6 +212,26 @@
76 return !value.toString().isNull();
77 }
78
79+QStringList AccountsService::keymaps() const
80+{
81+ auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES);
82+ QDBusArgument arg = value.value<QDBusArgument>();
83+ StringMapList maps = qdbus_cast<StringMapList>(arg);
84+ QStringList simplifiedMaps;
85+
86+ Q_FOREACH(const StringMap &map, maps) {
87+ Q_FOREACH(const QString &entry, map) {
88+ simplifiedMaps.append(entry);
89+ }
90+ }
91+
92+ if (!simplifiedMaps.isEmpty()) {
93+ return simplifiedMaps;
94+ }
95+
96+ return {QStringLiteral("us")};
97+}
98+
99 uint AccountsService::failedLogins() const
100 {
101 return getProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS).toUInt();
102
103=== modified file 'plugins/AccountsService/AccountsService.h'
104--- plugins/AccountsService/AccountsService.h 2016-03-11 17:34:24 +0000
105+++ plugins/AccountsService/AccountsService.h 2016-03-11 17:34:24 +0000
106@@ -22,6 +22,7 @@
107 #include <QHash>
108 #include <QObject>
109 #include <QString>
110+#include <QStringList>
111 #include <QVariant>
112
113 class AccountsServiceDBusAdaptor;
114@@ -68,6 +69,9 @@
115 Q_PROPERTY(bool hereLicensePathValid // qml sees a null string as "", so we use proxy setting for that
116 READ hereLicensePathValid
117 NOTIFY hereLicensePathChanged)
118+ Q_PROPERTY(QStringList keymaps
119+ READ keymaps
120+ NOTIFY keymapsChanged)
121
122 public:
123 enum PasswordDisplayHint {
124@@ -93,6 +97,7 @@
125 void setHereEnabled(bool enabled);
126 QString hereLicensePath() const;
127 bool hereLicensePathValid() const;
128+ QStringList keymaps() const;
129
130 Q_SIGNALS:
131 void userChanged();
132@@ -105,6 +110,7 @@
133 void failedLoginsChanged();
134 void hereEnabledChanged();
135 void hereLicensePathChanged();
136+ void keymapsChanged();
137
138 private Q_SLOTS:
139 void onPropertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
140
141=== modified file 'qml/Shell.qml'
142--- qml/Shell.qml 2016-03-11 17:34:24 +0000
143+++ qml/Shell.qml 2016-03-11 17:34:24 +0000
144@@ -102,6 +102,8 @@
145 // internal props from here onwards
146 readonly property var mainApp:
147 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null
148+ readonly property var mainAppWindow:
149+ applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindow : null
150
151 // Disable everything while greeter is waiting, so that the user can't swipe
152 // the greeter or launcher until we know whether the session is locked.
153@@ -737,6 +739,57 @@
154 onMouseMoved: { cursor.opacity = 1; }
155 }
156
157+ // keymap switching
158+ GlobalShortcut {
159+ shortcut: Qt.MetaModifier|Qt.Key_Space
160+ onTriggered: keymapPriv.nextKeymap()
161+ active: keymapPriv.keymapCount > 1
162+ }
163+
164+ GlobalShortcut {
165+ shortcut: Qt.MetaModifier|Qt.ShiftModifier|Qt.Key_Space
166+ onTriggered: keymapPriv.previousKeymap()
167+ active: keymapPriv.keymapCount > 1
168+ }
169+
170+ QtObject {
171+ id: keymapPriv
172+
173+ readonly property var keymaps: AccountsService.keymaps
174+ readonly property int keymapCount: keymaps.length
175+ readonly property int activeKeymapIndex: mainAppWindow ? keymaps.indexOf(mainAppWindow.activeKeymap) : 0 // the one that the window currently has
176+ property int currentKeymapIndex: 0 // the new one that we're setting
177+ onCurrentKeymapIndexChanged: switchToKeymap();
178+
179+ function nextKeymap() {
180+ var nextIndex = 0;
181+
182+ if (activeKeymapIndex !== -1 && activeKeymapIndex < keymapCount - 1) {
183+ nextIndex = activeKeymapIndex + 1;
184+ }
185+ print("!!! next keymap:", currentKeymapIndex, "->", nextIndex);
186+ currentKeymapIndex = nextIndex;
187+ }
188+
189+ function previousKeymap() {
190+ var prevIndex = keymapCount - 1;
191+
192+ if (activeKeymapIndex > 0) {
193+ prevIndex = activeKeymapIndex - 1;
194+ }
195+ print("!!! prev keymap:", currentKeymapIndex, "->", prevIndex);
196+ currentKeymapIndex = prevIndex;
197+ }
198+
199+ function switchToKeymap() {
200+ if (mainAppWindow) {
201+ mainAppWindow.switchToKeymap(keymaps[currentKeymapIndex]);
202+ }
203+ }
204+ }
205+
206+ onMainAppWindowChanged: keymapPriv.switchToKeymap()
207+
208 Rectangle {
209 id: shutdownFadeOutRectangle
210 z: cursor.z + 1
211
212=== modified file 'qml/Stages/AbstractStage.qml'
213--- qml/Stages/AbstractStage.qml 2016-03-11 17:34:24 +0000
214+++ qml/Stages/AbstractStage.qml 2016-03-11 17:34:24 +0000
215@@ -44,6 +44,7 @@
216
217 // To be read from outside
218 property var mainApp: null
219+ property var mainAppWindow: null
220 property int mainAppWindowOrientationAngle
221 property bool orientationChangesEnabled
222 property int supportedOrientations: Qt.PortraitOrientation
223
224=== modified file 'qml/Stages/ApplicationWindow.qml'
225--- qml/Stages/ApplicationWindow.qml 2016-02-12 00:10:54 +0000
226+++ qml/Stages/ApplicationWindow.qml 2016-03-11 17:34:24 +0000
227@@ -29,6 +29,7 @@
228 property bool orientationChangesEnabled: d.supportsSurfaceResize ? d.surfaceOldEnoughToBeResized : true
229 readonly property string title: sessionContainer.surface && sessionContainer.surface.name !== "" ?
230 sessionContainer.surface.name : d.name
231+ readonly property string activeKeymap: sessionContainer.surfaceContainer ? sessionContainer.surfaceContainer.activeKeymap : "us"
232
233 // to be set from outside
234 property QtObject application
235@@ -37,6 +38,10 @@
236 property int requestedWidth: -1
237 property int requestedHeight: -1
238
239+ function switchToKeymap(keymap) {
240+ sessionContainer.surfaceContainer.switchToKeymap(keymap);
241+ }
242+
243 readonly property int minimumWidth: sessionContainer.surface ? sessionContainer.surface.minimumWidth : 0
244 readonly property int minimumHeight: sessionContainer.surface ? sessionContainer.surface.minimumHeight : 0
245 readonly property int maximumWidth: sessionContainer.surface ? sessionContainer.surface.maximumWidth : 0
246
247=== modified file 'qml/Stages/DesktopStage.qml'
248--- qml/Stages/DesktopStage.qml 2016-03-11 17:34:24 +0000
249+++ qml/Stages/DesktopStage.qml 2016-03-11 17:34:24 +0000
250@@ -40,6 +40,8 @@
251 ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
252 : null
253
254+ mainAppWindow: priv.focusedAppDelegate ? priv.focusedAppDelegate.appWindow : null
255+
256 // application windows never rotate independently
257 mainAppWindowOrientationAngle: shellOrientationAngle
258
259@@ -284,6 +286,8 @@
260 property bool visuallyMaximized: false
261 property bool visuallyMinimized: false
262
263+ readonly property alias appWindow: decoratedWindow.window
264+
265 onFocusChanged: {
266 if (focus && ApplicationManager.focusedApplicationId !== appId) {
267 ApplicationManager.focusApplication(appId);
268
269=== modified file 'qml/Stages/PhoneStage.qml'
270--- qml/Stages/PhoneStage.qml 2016-03-11 17:34:24 +0000
271+++ qml/Stages/PhoneStage.qml 2016-03-11 17:34:24 +0000
272@@ -111,6 +111,8 @@
273 ? applicationManager.findApplication(applicationManager.focusedApplicationId)
274 : null
275
276+ mainAppWindow: priv.focusedAppDelegate ? priv.focusedAppDelegate.appWindow : null
277+
278 orientationChangesEnabled: priv.focusedAppOrientationChangesEnabled
279 && !priv.focusedAppDelegateIsDislocated
280 && !(priv.focusedAppDelegate && priv.focusedAppDelegate.xBehavior.running)
281
282=== modified file 'qml/Stages/SpreadDelegate.qml'
283--- qml/Stages/SpreadDelegate.qml 2016-03-11 17:34:24 +0000
284+++ qml/Stages/SpreadDelegate.qml 2016-03-11 17:34:24 +0000
285@@ -32,6 +32,7 @@
286 readonly property alias appWindowOrientationAngle: appWindowWithShadow.orientationAngle
287 readonly property alias appWindowRotation: appWindowWithShadow.rotation
288 readonly property alias orientationChangesEnabled: appWindow.orientationChangesEnabled
289+ readonly property alias appWindow: appWindow
290
291 // to be set from outside
292 property bool interactive: true
293
294=== modified file 'qml/Stages/SurfaceContainer.qml'
295--- qml/Stages/SurfaceContainer.qml 2015-11-30 12:18:40 +0000
296+++ qml/Stages/SurfaceContainer.qml 2016-03-11 17:34:24 +0000
297@@ -19,6 +19,7 @@
298 import Ubuntu.Gestures 0.1 // For TouchGate
299 import Utils 0.1 // for InputWatcher
300 import Unity.Application 0.1 // for MirSurfaceItem
301+import AccountsService 0.1
302
303 FocusScope {
304 id: root
305@@ -34,10 +35,25 @@
306 property int requestedWidth: -1
307 property int requestedHeight: -1
308
309+ readonly property string activeKeymap: surface ? (surface.keymapVariant !== "" ? surface.keymapLayout + "+" + surface.keymapVariant : surface.keymapLayout)
310+ : "us"
311+ property string savedKeymap: AccountsService.keymaps[0] // start with the user default
312+
313 onSurfaceChanged: {
314 if (surface) {
315 surfaceItem.surface = surface;
316 root.hadSurface = false;
317+ switchToKeymap(savedKeymap);
318+ }
319+ }
320+
321+ function switchToKeymap(keymap) {
322+ var finalKeymap = keymap.split("+");
323+ savedKeymap = keymap; // save the keymap in case the surface changes later
324+
325+ if (surface) {
326+ print("!!! Switching to keymap", keymap);
327+ surface.setKeymap(finalKeymap[0], finalKeymap[1] || "");
328 }
329 }
330
331
332=== modified file 'qml/Stages/TabletStage.qml'
333--- qml/Stages/TabletStage.qml 2016-03-11 17:34:24 +0000
334+++ qml/Stages/TabletStage.qml 2016-03-11 17:34:24 +0000
335@@ -76,6 +76,8 @@
336 }
337 }
338
339+ mainAppWindow: priv.focusedAppDelegate ? priv.focusedAppDelegate.appWindow : null
340+
341 orientationChangesEnabled: priv.mainAppOrientationChangesEnabled
342
343 supportedOrientations: mainApp ? mainApp.supportedOrientations
344
345=== modified file 'tests/mocks/AccountsService/AccountsService.cpp'
346--- tests/mocks/AccountsService/AccountsService.cpp 2015-09-22 10:44:21 +0000
347+++ tests/mocks/AccountsService/AccountsService.cpp 2016-03-11 17:34:24 +0000
348@@ -152,3 +152,20 @@
349 {
350 return !m_hereLicensePath.isNull();
351 }
352+
353+QStringList AccountsService::keymaps() const
354+{
355+ if (!m_kbdMap.isEmpty()) {
356+ return m_kbdMap;
357+ }
358+
359+ return {QStringLiteral("us")};
360+}
361+
362+void AccountsService::setKeymaps(const QStringList &keymaps)
363+{
364+ if (keymaps != m_kbdMap) {
365+ m_kbdMap = keymaps;
366+ Q_EMIT keymapsChanged();
367+ }
368+}
369
370=== modified file 'tests/mocks/AccountsService/AccountsService.h'
371--- tests/mocks/AccountsService/AccountsService.h 2014-11-24 17:42:36 +0000
372+++ tests/mocks/AccountsService/AccountsService.h 2016-03-11 17:34:24 +0000
373@@ -70,6 +70,10 @@
374 Q_PROPERTY(bool hereLicensePathValid // qml sees a null string as "", so we use proxy setting for that
375 READ hereLicensePathValid
376 NOTIFY hereLicensePathChanged)
377+ Q_PROPERTY(QStringList keymaps
378+ READ keymaps
379+ WRITE setKeymaps // only in mock
380+ NOTIFY keymapsChanged)
381
382 public:
383 enum PasswordDisplayHint {
384@@ -99,6 +103,8 @@
385 QString hereLicensePath() const;
386 void setHereLicensePath(const QString &path);
387 bool hereLicensePathValid() const;
388+ QStringList keymaps() const;
389+ void setKeymaps(const QStringList &keymaps);
390
391 Q_SIGNALS:
392 void userChanged();
393@@ -111,6 +117,7 @@
394 void failedLoginsChanged();
395 void hereEnabledChanged();
396 void hereLicensePathChanged();
397+ void keymapsChanged();
398
399 private:
400 bool m_enableLauncherWhileLocked;
401@@ -122,6 +129,7 @@
402 bool m_demoEdges;
403 bool m_hereEnabled;
404 QString m_hereLicensePath;
405+ QStringList m_kbdMap;
406 };
407
408 #endif
409
410=== modified file 'tests/mocks/QMenuModel/QDBusActionGroup.qml'
411--- tests/mocks/QMenuModel/QDBusActionGroup.qml 2015-07-21 14:38:35 +0000
412+++ tests/mocks/QMenuModel/QDBusActionGroup.qml 2016-03-11 17:34:24 +0000
413@@ -1,8 +1,5 @@
414 /*
415- * Copyright (C) 2013 Canonical, Ltd.
416- *
417- * Authors:
418- * Daniel d'Andrada <daniel.dandrada@canonical.com>
419+ * Copyright (C) 2013-2016 Canonical, Ltd.
420 *
421 * This program is free software; you can redistribute it and/or modify
422 * it under the terms of the GNU General Public License as published by
423@@ -18,7 +15,6 @@
424 */
425
426 import QtQuick 2.4
427-import Ubuntu.Settings.Menus 0.1 as Menus
428 import QMenuModel 0.1
429
430 QtObject {
431@@ -47,6 +43,10 @@
432 function activate() {
433 activated();
434 }
435+
436+ function updateState(newState) {
437+ state = newState;
438+ }
439 }", actionGroup);
440 }
441 }
442
443=== modified file 'tests/mocks/Unity/Application/MirSurface.cpp'
444--- tests/mocks/Unity/Application/MirSurface.cpp 2016-03-11 17:34:24 +0000
445+++ tests/mocks/Unity/Application/MirSurface.cpp 2016-03-11 17:34:24 +0000
446@@ -133,7 +133,24 @@
447 Q_EMIT orientationAngleChanged(angle);
448 }
449
450-
451+QString MirSurface::keymapLayout() const
452+{
453+ return m_keyMap.first;
454+}
455+
456+QString MirSurface::keymapVariant() const
457+{
458+ return m_keyMap.second;
459+}
460+
461+void MirSurface::setKeymap(const QString &layout, const QString &variant)
462+{
463+ if (layout.isEmpty()) {
464+ return;
465+ }
466+ m_keyMap = qMakePair(layout, variant);
467+ Q_EMIT keymapChanged(layout, variant);
468+}
469
470 void MirSurface::registerView(qintptr viewId)
471 {
472
473=== modified file 'tests/mocks/Unity/Application/MirSurface.h'
474--- tests/mocks/Unity/Application/MirSurface.h 2016-03-11 17:34:24 +0000
475+++ tests/mocks/Unity/Application/MirSurface.h 2016-03-11 17:34:24 +0000
476@@ -74,6 +74,10 @@
477 int widthIncrement() const override { return m_widthIncrement; }
478 int heightIncrement() const override { return m_heightIncrement; }
479
480+ QString keymapLayout() const override;
481+ QString keymapVariant() const override;
482+ Q_INVOKABLE void setKeymap(const QString &layout, const QString &variant) override;
483+
484 ////
485 // API for tests
486
487@@ -156,6 +160,8 @@
488 bool visible;
489 };
490 QHash<qintptr, View> m_views;
491+
492+ QPair<QString,QString> m_keyMap; // pair of layout+variant
493 };
494
495 #endif // MOCK_MIR_SURFACE_H
496
497=== modified file 'tests/plugins/AccountsService/PropertiesServer.cpp'
498--- tests/plugins/AccountsService/PropertiesServer.cpp 2016-03-11 17:34:24 +0000
499+++ tests/plugins/AccountsService/PropertiesServer.cpp 2016-03-11 17:34:24 +0000
500@@ -25,10 +25,16 @@
501 #include <QDBusMessage>
502 #include <QDBusMetaType>
503
504+using StringMap = QMap<QString,QString>;
505+using StringMapList = QList<StringMap>;
506+Q_DECLARE_METATYPE(StringMapList)
507+
508 PropertiesServer::PropertiesServer(QObject *parent)
509 : QObject(parent)
510 {
511 qDBusRegisterMetaType<QList<QVariantMap>>();
512+ qDBusRegisterMetaType<StringMap>();
513+ qDBusRegisterMetaType<StringMapList>();
514 Reset();
515 }
516
517@@ -63,6 +69,8 @@
518 if (interface == QStringLiteral("com.canonical.unity.AccountsService") &&
519 property == QStringLiteral("LauncherItems")) {
520 newValue = QVariant::fromValue(qdbus_cast<QList<QVariantMap>>(newValue.value<QDBusArgument>()));
521+ } else if (interface == "org.freedesktop.Accounts.User" && property == "InputSources") {
522+ newValue = QVariant::fromValue(qdbus_cast<StringMapList>(newValue.value<QDBusArgument>()));
523 }
524
525 oldValue = newValue;
526@@ -100,4 +108,5 @@
527 m_properties["com.ubuntu.location.providers.here.AccountsService"]["LicenseBasePath"] = "";
528 m_properties["org.freedesktop.Accounts.User"]["BackgroundFile"] = "";
529 m_properties["org.freedesktop.Accounts.User"]["RealName"] = "";
530+ m_properties["org.freedesktop.Accounts.User"]["InputSources"] = QVariant::fromValue(StringMapList());
531 }
532
533=== modified file 'tests/plugins/AccountsService/PropertiesServer.h'
534--- tests/plugins/AccountsService/PropertiesServer.h 2016-03-11 17:34:24 +0000
535+++ tests/plugins/AccountsService/PropertiesServer.h 2016-03-11 17:34:24 +0000
536@@ -20,7 +20,6 @@
537 #ifndef UNITY_PROPERTIESSERVER_H
538 #define UNITY_PROPERTIESSERVER_H
539
540-#include "PropertiesServer.h"
541 #include <QDBusContext>
542 #include <QDBusVariant>
543 #include <QObject>
544
545=== modified file 'tests/plugins/AccountsService/client.cpp'
546--- tests/plugins/AccountsService/client.cpp 2016-03-11 17:34:24 +0000
547+++ tests/plugins/AccountsService/client.cpp 2016-03-11 17:34:24 +0000
548@@ -23,6 +23,11 @@
549 #include <QTest>
550 #include <QDebug>
551 #include <QDBusReply>
552+#include <QDBusMetaType>
553+
554+using StringMap = QMap<QString,QString>;
555+using StringMapList = QList<StringMap>;
556+Q_DECLARE_METATYPE(StringMapList)
557
558 template <class T>
559 QVariant dbusVariant(const T& value) { return QVariant::fromValue(QDBusVariant(value)); }
560@@ -52,6 +57,9 @@
561
562 QObject::connect(m_uscInputInterface, SIGNAL(setMousePrimaryButtonCalled(int)),
563 this, SIGNAL(setMousePrimaryButtonCalled(int)));
564+
565+ qDBusRegisterMetaType<StringMap>();
566+ qDBusRegisterMetaType<StringMapList>();
567 }
568
569 private Q_SLOTS:
570@@ -266,6 +274,28 @@
571 QCOMPARE(arguments.at(0).toInt(), 0);
572 }
573
574+ void testAsynchronousChangeForKeymaps()
575+ {
576+ AccountsService session(this, QTest::currentTestFunction());
577+
578+ QCOMPARE(session.keymaps(), {"us"});
579+
580+ StringMapList inputSources;
581+ StringMap map1;
582+ map1.insert("xkb", "cz+qwerty");
583+ inputSources.append(map1);
584+ StringMap map2;
585+ map2.insert("xkb", "fr");
586+ inputSources.append(map2);
587+
588+ ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
589+ "org.freedesktop.Accounts.User",
590+ "InputSources",
591+ QVariant::fromValue(QDBusVariant(QVariant::fromValue(inputSources)))));
592+ QStringList result = {"cz+qwerty", "fr"};
593+ QTRY_COMPARE(session.keymaps(), result);
594+ }
595+
596 Q_SIGNALS:
597 void propertiesChanged(const QString &interface, const QVariantMap &changed, const QStringList &invalid);
598 void setMousePrimaryButtonCalled(int button);
599
600=== modified file 'tests/qmltests/Stages/tst_DesktopStage.qml'
601--- tests/qmltests/Stages/tst_DesktopStage.qml 2016-03-11 17:34:24 +0000
602+++ tests/qmltests/Stages/tst_DesktopStage.qml 2016-03-11 17:34:24 +0000
603@@ -21,6 +21,7 @@
604 import Unity.Application 0.1
605 import Unity.Test 0.1
606 import Utils 0.1
607+import AccountsService 0.1
608
609 import ".." // For EdgeBarrierControls
610 import "../../../qml/Stages"
611@@ -130,6 +131,11 @@
612 }
613 }
614
615+ SignalSpy {
616+ id: keymapSpy
617+ signalName: "activated"
618+ }
619+
620 UnityTestCase {
621 id: testCase
622 name: "DesktopStage"
623@@ -510,8 +516,6 @@
624 }
625
626 function test_dropShadow() {
627- killAllRunningApps();
628-
629 // verify the drop shadow is not visible initially
630 verify(PanelState.dropShadow == false);
631
632@@ -536,5 +540,50 @@
633 // verify the drop shadow is gone
634 verify(PanelState.dropShadow == false);
635 }
636+
637+ function test_switchKeymap() {
638+ AccountsService.keymaps = ["cz+qwerty", "fr", "us"] // "configure" the keymaps for user
639+
640+ var facebookApp = startApplication("facebook-webapp");
641+ var appSurface = facebookApp.session.lastSurface;
642+ verify(appSurface);
643+
644+ // verify the initial keymap is the first one from the list
645+ tryCompare(appSurface, "keymapLayout", AccountsService.keymaps[0].split("+")[0]); // cz
646+ tryCompare(appSurface, "keymapVariant", AccountsService.keymaps[0].split("+")[1]); // qwerty
647+
648+ var facebookWindow = desktopStage.mainAppWindow;
649+ verify(facebookWindow);
650+
651+ // switch to next keymap (should be "fr")
652+ facebookWindow.switchToKeymap(1);
653+ var frKeymap = AccountsService.keymaps[1].split("+");
654+ tryCompare(appSurface, "keymapLayout", frKeymap[0]);
655+ tryCompare(appSurface, "keymapVariant", "");
656+
657+ // verify the surface reports the same keymap as the ApplicationWindow
658+ tryCompare(appSurface, "keymapLayout", facebookWindow.activeKeymap);
659+
660+ // switch to next keymap again (should be "us")
661+ facebookWindow.switchToKeymap(2);
662+ tryCompare(facebookWindow, "activeKeymap", "us");
663+ }
664+
665+ function test_switchKeymapShortcuts() {
666+ var keymapActionGroup = findInvisibleChild(desktopStage, "keymapActionGroup");
667+ verify(keymapActionGroup);
668+
669+ keymapSpy.target = keymapActionGroup.nextAction;
670+
671+ // switch to next keymap
672+ keyClick(Qt.Key_Space, Qt.MetaModifier);
673+ tryCompare(keymapSpy, "count", 1);
674+
675+ keymapSpy.clear();
676+
677+ // switch to previous keymap
678+ keyClick(Qt.Key_Space, Qt.MetaModifier|Qt.ShiftModifier);
679+ tryCompare(keymapSpy, "count", 1);
680+ }
681 }
682 }

Subscribers

People subscribed via source and target branches