Merge lp:~unity-team/unity8/keymapSwitching into lp:unity8
- keymapSwitching
- Merge into trunk
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 | ||||||||||||
Related bugs: |
|
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
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
Michael Terry (mterry) wrote : | # |
Some inline comments. Haven't reviewed test code yet. Am still setting up my phone to test these branches there.
- 2276. By Lukáš Tinkl
-
drop unneeded changes
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2276
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 2277. By Lukáš Tinkl
-
move the keymap switching to Shell
to survive Stage switching
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2277
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 2278. By Michał Sawicz
- 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
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 | } |
FAILED: Continuous integration, rev:2275 /unity8- jenkins. ubuntu. com/job/ lp-unity8- ci/674/ /unity8- jenkins. ubuntu. com/job/ build-0- fetch/887 /unity8- jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= vivid+overlay/ 903 /unity8- jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= xenial/ 903 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= vivid+overlay/ 901/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= xenial/ 901/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= vivid+overlay/ 901/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= xenial/ 901/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= vivid+overlay/ 901/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= xenial/ 901/console
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /unity8- jenkins. ubuntu. com/job/ lp-unity8- ci/674/ rebuild
https:/