Merge lp:~unity-team/unity8/keymapSwitching into lp:unity8
- keymapSwitching
- Merge into trunk
Status: | Merged | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Approved by: | Michael Terry | ||||||||||||
Approved revision: | 2281 | ||||||||||||
Merged at revision: | 2291 | ||||||||||||
Proposed branch: | lp:~unity-team/unity8/keymapSwitching | ||||||||||||
Merge into: | lp:unity8 | ||||||||||||
Prerequisite: | lp:~unity-team/unity8/shell_chrome | ||||||||||||
Diff against target: |
634 lines (+265/-10) 20 files modified
plugins/AccountsService/AccountsService.cpp (+26/-0) plugins/AccountsService/AccountsService.h (+6/-0) qml/Shell.qml (+50/-0) qml/Stages/AbstractStage.qml (+1/-0) qml/Stages/ApplicationWindow.qml (+4/-0) qml/Stages/DesktopStage.qml (+4/-0) qml/Stages/PhoneStage.qml (+2/-0) qml/Stages/SpreadDelegate.qml (+1/-0) qml/Stages/SurfaceContainer.qml (+13/-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/-2) tests/mocks/Unity/Application/MirSurface.h (+5/-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 (+0/-2) tests/qmltests/tst_Shell.qml (+64/-0) |
||||||||||||
To merge this branch: | bzr merge lp:~unity-team/unity8/keymapSwitching | ||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Daniel d'Andrada (community) | Needs Information | ||
Michael Terry | Approve | ||
Unity8 CI Bot | continuous-integration | Approve | |
Review via email: mp+288842@code.launchpad.net |
This proposal supersedes 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 : Posted in a previous version of this proposal | # |
Michael Terry (mterry) wrote : Posted in a previous version of this proposal | # |
Some inline comments. Haven't reviewed test code yet. Am still setting up my phone to test these branches there.
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
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:/
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
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:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2278
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 2279. By Lukáš Tinkl
-
refine test for switching keymaps, move to tst_Shell
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2279
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:/
Michael Terry (mterry) wrote : | # |
As mentioned in telegram, I think we can drop activeKeymap. And the qmltest should test going past the front / end of keymap list. But besides that, it looks fine. Haven't tested yet, waiting for silo to rebuild.
- 2280. By Lukáš Tinkl
-
remove activeKeymapIndex, refine tests
- 2281. By Lukáš Tinkl
-
remove debug
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2280
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:/
Lukáš Tinkl (lukas-kde) wrote : | # |
> As mentioned in telegram, I think we can drop activeKeymap. And the qmltest
> should test going past the front / end of keymap list. But besides that, it
> looks fine. Haven't tested yet, waiting for silo to rebuild.
Fixed both issues
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:2281
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Michael Terry (mterry) wrote : | # |
Note to self:
sudo gdbus call --system --dest org.freedesktop
Michael Terry (mterry) wrote : | # |
OK, worked fine in my testing. Modulo a crasher if you give invalid input. (bug 1557634) But that's not a blocker for now.
Thanks!
Daniel d'Andrada (dandrader) wrote : | # |
"""
function switchToKeymap(
var finalKeymap = keymap.split("+");
savedKeymap = keymap; // save the keymap in case the surface changes later
if (surface) {
}
}
"""
I know I'm late to the party, but as I'm rebasing surface-based WM on top of silo 041 I couldn't help frowning when I saw that.
This kind of imperative work doesn't belong to QML. Can't the surface have a property that takes the full string and does any and all parsing needed behind the scenes?
Something like that:
"""
SurfaceContainer {
id: root
property string keymap
Binding {
target: surface
property: "keymap"
value: root.keymap
}
}
"""
Daniel d'Andrada (dandrader) wrote : | # |
And as a continuation of the idea in my previous comment, in ApplicationWindow:
"""
function switchToKeymap(
}
"""
Would be something like:
"""
property alias keymap: sessionContaine
"""
Lukáš Tinkl (lukas-kde) wrote : | # |
Yeah, thanks for the comments, I'm totally aware this will need some rework with surface based WM (this and the switching based on the currently focused _app_, not surface).
Daniel d'Andrada (dandrader) wrote : | # |
My comments are not related to surface-based versus application-based WM. They are about imperative versus declarative code.
Preview Diff
1 | === modified file 'plugins/AccountsService/AccountsService.cpp' |
2 | --- plugins/AccountsService/AccountsService.cpp 2016-03-14 17:35:28 +0000 |
3 | +++ plugins/AccountsService/AccountsService.cpp 2016-03-14 17:35:29 +0000 |
4 | @@ -36,6 +36,7 @@ |
5 | #define PROP_ENABLE_INDICATORS_WHILE_LOCKED QStringLiteral("EnableIndicatorsWhileLocked") |
6 | #define PROP_ENABLE_LAUNCHER_WHILE_LOCKED QStringLiteral("EnableLauncherWhileLocked") |
7 | #define PROP_FAILED_LOGINS QStringLiteral("FailedLogins") |
8 | +#define PROP_INPUT_SOURCES QStringLiteral("InputSources") |
9 | #define PROP_LICENSE_ACCEPTED QStringLiteral("LicenseAccepted") |
10 | #define PROP_LICENSE_BASE_PATH QStringLiteral("LicenseBasePath") |
11 | #define PROP_MOUSE_CURSOR_SPEED QStringLiteral("MouseCursorSpeed") |
12 | @@ -54,6 +55,10 @@ |
13 | #define PROP_TOUCHPAD_TAP_TO_CLICK QStringLiteral("TouchpadTapToClick") |
14 | #define PROP_TOUCHPAD_TWO_FINGER_SCROLL QStringLiteral("TouchpadTwoFingerScroll") |
15 | |
16 | +using StringMap = QMap<QString,QString>; |
17 | +using StringMapList = QList<StringMap>; |
18 | +Q_DECLARE_METATYPE(StringMapList) |
19 | + |
20 | |
21 | QVariant primaryButtonConverter(const QVariant &value) |
22 | { |
23 | @@ -82,6 +87,7 @@ |
24 | registerProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE, QStringLiteral("backgroundFileChanged")); |
25 | registerProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, QStringLiteral("emailChanged")); |
26 | registerProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, QStringLiteral("realNameChanged")); |
27 | + registerProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES, QStringLiteral("keymapsChanged")); |
28 | registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, QStringLiteral("hereEnabledChanged")); |
29 | registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH, QStringLiteral("hereLicensePathChanged")); |
30 | registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED, QStringLiteral("enableLauncherWhileLockedChanged")); |
31 | @@ -230,6 +236,26 @@ |
32 | setProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, email); |
33 | } |
34 | |
35 | +QStringList AccountsService::keymaps() const |
36 | +{ |
37 | + auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES); |
38 | + QDBusArgument arg = value.value<QDBusArgument>(); |
39 | + StringMapList maps = qdbus_cast<StringMapList>(arg); |
40 | + QStringList simplifiedMaps; |
41 | + |
42 | + Q_FOREACH(const StringMap &map, maps) { |
43 | + Q_FOREACH(const QString &entry, map) { |
44 | + simplifiedMaps.append(entry); |
45 | + } |
46 | + } |
47 | + |
48 | + if (!simplifiedMaps.isEmpty()) { |
49 | + return simplifiedMaps; |
50 | + } |
51 | + |
52 | + return {QStringLiteral("us")}; |
53 | +} |
54 | + |
55 | uint AccountsService::failedLogins() const |
56 | { |
57 | return getProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS).toUInt(); |
58 | |
59 | === modified file 'plugins/AccountsService/AccountsService.h' |
60 | --- plugins/AccountsService/AccountsService.h 2016-03-14 17:35:28 +0000 |
61 | +++ plugins/AccountsService/AccountsService.h 2016-03-14 17:35:29 +0000 |
62 | @@ -20,6 +20,7 @@ |
63 | #include <QHash> |
64 | #include <QObject> |
65 | #include <QString> |
66 | +#include <QStringList> |
67 | #include <QVariant> |
68 | |
69 | class AccountsServiceDBusAdaptor; |
70 | @@ -68,6 +69,9 @@ |
71 | NOTIFY hereLicensePathChanged) |
72 | Q_PROPERTY(QString realName READ realName WRITE setRealName NOTIFY realNameChanged) |
73 | Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY emailChanged) |
74 | + Q_PROPERTY(QStringList keymaps |
75 | + READ keymaps |
76 | + NOTIFY keymapsChanged) |
77 | |
78 | public: |
79 | enum PasswordDisplayHint { |
80 | @@ -97,6 +101,7 @@ |
81 | void setRealName(const QString &realName); |
82 | QString email() const; |
83 | void setEmail(const QString &email); |
84 | + QStringList keymaps() const; |
85 | |
86 | Q_SIGNALS: |
87 | void userChanged(); |
88 | @@ -111,6 +116,7 @@ |
89 | void hereLicensePathChanged(); |
90 | void realNameChanged(); |
91 | void emailChanged(); |
92 | + void keymapsChanged(); |
93 | |
94 | private Q_SLOTS: |
95 | void onPropertiesChanged(const QString &user, const QString &interface, const QStringList &changed); |
96 | |
97 | === modified file 'qml/Shell.qml' |
98 | --- qml/Shell.qml 2016-03-14 17:35:28 +0000 |
99 | +++ qml/Shell.qml 2016-03-14 17:35:29 +0000 |
100 | @@ -102,6 +102,8 @@ |
101 | // internal props from here onwards |
102 | readonly property var mainApp: |
103 | applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null |
104 | + readonly property var mainAppWindow: |
105 | + applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindow : null |
106 | |
107 | // Disable everything while greeter is waiting, so that the user can't swipe |
108 | // the greeter or launcher until we know whether the session is locked. |
109 | @@ -736,6 +738,54 @@ |
110 | onMouseMoved: { cursor.opacity = 1; } |
111 | } |
112 | |
113 | + // keymap switching |
114 | + GlobalShortcut { |
115 | + shortcut: Qt.MetaModifier|Qt.Key_Space |
116 | + onTriggered: keymapPriv.nextKeymap() |
117 | + active: keymapPriv.keymapCount > 1 |
118 | + } |
119 | + |
120 | + GlobalShortcut { |
121 | + shortcut: Qt.MetaModifier|Qt.ShiftModifier|Qt.Key_Space |
122 | + onTriggered: keymapPriv.previousKeymap() |
123 | + active: keymapPriv.keymapCount > 1 |
124 | + } |
125 | + |
126 | + QtObject { |
127 | + id: keymapPriv |
128 | + |
129 | + readonly property var keymaps: AccountsService.keymaps |
130 | + readonly property int keymapCount: keymaps.length |
131 | + property int currentKeymapIndex: 0 // the new one that we're setting |
132 | + onCurrentKeymapIndexChanged: switchToKeymap(); |
133 | + |
134 | + function nextKeymap() { |
135 | + var nextIndex = 0; |
136 | + |
137 | + if (currentKeymapIndex !== -1 && currentKeymapIndex < keymapCount - 1) { |
138 | + nextIndex = currentKeymapIndex + 1; |
139 | + } |
140 | + currentKeymapIndex = nextIndex; |
141 | + } |
142 | + |
143 | + function previousKeymap() { |
144 | + var prevIndex = keymapCount - 1; |
145 | + |
146 | + if (currentKeymapIndex > 0) { |
147 | + prevIndex = currentKeymapIndex - 1; |
148 | + } |
149 | + currentKeymapIndex = prevIndex; |
150 | + } |
151 | + |
152 | + function switchToKeymap() { |
153 | + if (mainAppWindow) { |
154 | + mainAppWindow.switchToKeymap(keymaps[currentKeymapIndex]); |
155 | + } |
156 | + } |
157 | + } |
158 | + |
159 | + onMainAppWindowChanged: keymapPriv.switchToKeymap() |
160 | + |
161 | Rectangle { |
162 | id: shutdownFadeOutRectangle |
163 | z: cursor.z + 1 |
164 | |
165 | === modified file 'qml/Stages/AbstractStage.qml' |
166 | --- qml/Stages/AbstractStage.qml 2016-03-14 17:35:28 +0000 |
167 | +++ qml/Stages/AbstractStage.qml 2016-03-14 17:35:29 +0000 |
168 | @@ -44,6 +44,7 @@ |
169 | |
170 | // To be read from outside |
171 | property var mainApp: null |
172 | + property var mainAppWindow: null |
173 | property int mainAppWindowOrientationAngle: 0 |
174 | property bool orientationChangesEnabled |
175 | property int supportedOrientations: Qt.PortraitOrientation |
176 | |
177 | === modified file 'qml/Stages/ApplicationWindow.qml' |
178 | --- qml/Stages/ApplicationWindow.qml 2016-03-14 17:35:28 +0000 |
179 | +++ qml/Stages/ApplicationWindow.qml 2016-03-14 17:35:29 +0000 |
180 | @@ -39,6 +39,10 @@ |
181 | property int requestedWidth: -1 |
182 | property int requestedHeight: -1 |
183 | |
184 | + function switchToKeymap(keymap) { |
185 | + sessionContainer.surfaceContainer.switchToKeymap(keymap); |
186 | + } |
187 | + |
188 | readonly property int minimumWidth: sessionContainer.surface ? sessionContainer.surface.minimumWidth : 0 |
189 | readonly property int minimumHeight: sessionContainer.surface ? sessionContainer.surface.minimumHeight : 0 |
190 | readonly property int maximumWidth: sessionContainer.surface ? sessionContainer.surface.maximumWidth : 0 |
191 | |
192 | === modified file 'qml/Stages/DesktopStage.qml' |
193 | --- qml/Stages/DesktopStage.qml 2016-03-14 17:35:28 +0000 |
194 | +++ qml/Stages/DesktopStage.qml 2016-03-14 17:35:29 +0000 |
195 | @@ -40,6 +40,8 @@ |
196 | ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId) |
197 | : null |
198 | |
199 | + mainAppWindow: priv.focusedAppDelegate ? priv.focusedAppDelegate.appWindow : null |
200 | + |
201 | // application windows never rotate independently |
202 | mainAppWindowOrientationAngle: shellOrientationAngle |
203 | |
204 | @@ -284,6 +286,8 @@ |
205 | property bool visuallyMaximized: false |
206 | property bool visuallyMinimized: false |
207 | |
208 | + readonly property alias appWindow: decoratedWindow.window |
209 | + |
210 | onFocusChanged: { |
211 | if (focus && ApplicationManager.focusedApplicationId !== appId) { |
212 | ApplicationManager.focusApplication(appId); |
213 | |
214 | === modified file 'qml/Stages/PhoneStage.qml' |
215 | --- qml/Stages/PhoneStage.qml 2016-03-14 17:35:28 +0000 |
216 | +++ qml/Stages/PhoneStage.qml 2016-03-14 17:35:29 +0000 |
217 | @@ -111,6 +111,8 @@ |
218 | ? applicationManager.findApplication(applicationManager.focusedApplicationId) |
219 | : null |
220 | |
221 | + mainAppWindow: priv.focusedAppDelegate ? priv.focusedAppDelegate.appWindow : null |
222 | + |
223 | orientationChangesEnabled: priv.focusedAppOrientationChangesEnabled |
224 | && !priv.focusedAppDelegateIsDislocated |
225 | && !(priv.focusedAppDelegate && priv.focusedAppDelegate.xBehavior.running) |
226 | |
227 | === modified file 'qml/Stages/SpreadDelegate.qml' |
228 | --- qml/Stages/SpreadDelegate.qml 2016-03-14 17:35:28 +0000 |
229 | +++ qml/Stages/SpreadDelegate.qml 2016-03-14 17:35:29 +0000 |
230 | @@ -37,6 +37,7 @@ |
231 | | Qt.LandscapeOrientation |
232 | | Qt.InvertedPortraitOrientation |
233 | | Qt.InvertedLandscapeOrientation |
234 | + readonly property alias appWindow: appWindow |
235 | |
236 | // to be set from outside |
237 | property bool interactive: true |
238 | |
239 | === modified file 'qml/Stages/SurfaceContainer.qml' |
240 | --- qml/Stages/SurfaceContainer.qml 2016-03-14 17:35:28 +0000 |
241 | +++ qml/Stages/SurfaceContainer.qml 2016-03-14 17:35:29 +0000 |
242 | @@ -19,6 +19,7 @@ |
243 | import Ubuntu.Gestures 0.1 // For TouchGate |
244 | import Utils 0.1 // for InputWatcher |
245 | import Unity.Application 0.1 // for MirSurfaceItem |
246 | +import AccountsService 0.1 |
247 | |
248 | FocusScope { |
249 | id: root |
250 | @@ -34,10 +35,22 @@ |
251 | property int requestedWidth: -1 |
252 | property int requestedHeight: -1 |
253 | |
254 | + property string savedKeymap: AccountsService.keymaps[0] // start with the user default |
255 | + |
256 | onSurfaceChanged: { |
257 | if (surface) { |
258 | surfaceItem.surface = surface; |
259 | root.hadSurface = false; |
260 | + switchToKeymap(savedKeymap); |
261 | + } |
262 | + } |
263 | + |
264 | + function switchToKeymap(keymap) { |
265 | + var finalKeymap = keymap.split("+"); |
266 | + savedKeymap = keymap; // save the keymap in case the surface changes later |
267 | + |
268 | + if (surface) { |
269 | + surface.setKeymap(finalKeymap[0], finalKeymap[1] || ""); |
270 | } |
271 | } |
272 | |
273 | |
274 | === modified file 'qml/Stages/TabletStage.qml' |
275 | --- qml/Stages/TabletStage.qml 2016-03-14 17:35:28 +0000 |
276 | +++ qml/Stages/TabletStage.qml 2016-03-14 17:35:29 +0000 |
277 | @@ -76,6 +76,8 @@ |
278 | } |
279 | } |
280 | |
281 | + mainAppWindow: priv.focusedAppDelegate ? priv.focusedAppDelegate.appWindow : null |
282 | + |
283 | orientationChangesEnabled: priv.mainAppOrientationChangesEnabled |
284 | |
285 | supportedOrientations: { |
286 | |
287 | === modified file 'tests/mocks/AccountsService/AccountsService.cpp' |
288 | --- tests/mocks/AccountsService/AccountsService.cpp 2016-03-14 17:35:28 +0000 |
289 | +++ tests/mocks/AccountsService/AccountsService.cpp 2016-03-14 17:35:29 +0000 |
290 | @@ -172,3 +172,20 @@ |
291 | m_email = email; |
292 | Q_EMIT emailChanged(); |
293 | } |
294 | + |
295 | +QStringList AccountsService::keymaps() const |
296 | +{ |
297 | + if (!m_kbdMap.isEmpty()) { |
298 | + return m_kbdMap; |
299 | + } |
300 | + |
301 | + return {QStringLiteral("us")}; |
302 | +} |
303 | + |
304 | +void AccountsService::setKeymaps(const QStringList &keymaps) |
305 | +{ |
306 | + if (keymaps != m_kbdMap) { |
307 | + m_kbdMap = keymaps; |
308 | + Q_EMIT keymapsChanged(); |
309 | + } |
310 | +} |
311 | |
312 | === modified file 'tests/mocks/AccountsService/AccountsService.h' |
313 | --- tests/mocks/AccountsService/AccountsService.h 2016-03-14 17:35:28 +0000 |
314 | +++ tests/mocks/AccountsService/AccountsService.h 2016-03-14 17:35:29 +0000 |
315 | @@ -69,6 +69,10 @@ |
316 | NOTIFY hereLicensePathChanged) |
317 | Q_PROPERTY(QString realName READ realName WRITE setRealName NOTIFY realNameChanged) |
318 | Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY emailChanged) |
319 | + Q_PROPERTY(QStringList keymaps |
320 | + READ keymaps |
321 | + WRITE setKeymaps // only in mock |
322 | + NOTIFY keymapsChanged) |
323 | |
324 | public: |
325 | enum PasswordDisplayHint { |
326 | @@ -102,6 +106,8 @@ |
327 | void setRealName(const QString &realName); |
328 | QString email() const; |
329 | void setEmail(const QString &email); |
330 | + QStringList keymaps() const; |
331 | + void setKeymaps(const QStringList &keymaps); |
332 | |
333 | Q_SIGNALS: |
334 | void userChanged(); |
335 | @@ -116,6 +122,7 @@ |
336 | void hereLicensePathChanged(); |
337 | void realNameChanged(); |
338 | void emailChanged(); |
339 | + void keymapsChanged(); |
340 | |
341 | private: |
342 | bool m_enableLauncherWhileLocked; |
343 | @@ -128,6 +135,7 @@ |
344 | bool m_hereEnabled; |
345 | QString m_hereLicensePath; |
346 | QString m_realName; |
347 | + QStringList m_kbdMap; |
348 | QString m_email; |
349 | }; |
350 | |
351 | |
352 | === modified file 'tests/mocks/QMenuModel/QDBusActionGroup.qml' |
353 | --- tests/mocks/QMenuModel/QDBusActionGroup.qml 2015-07-21 14:38:35 +0000 |
354 | +++ tests/mocks/QMenuModel/QDBusActionGroup.qml 2016-03-14 17:35:29 +0000 |
355 | @@ -1,8 +1,5 @@ |
356 | /* |
357 | - * Copyright (C) 2013 Canonical, Ltd. |
358 | - * |
359 | - * Authors: |
360 | - * Daniel d'Andrada <daniel.dandrada@canonical.com> |
361 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
362 | * |
363 | * This program is free software; you can redistribute it and/or modify |
364 | * it under the terms of the GNU General Public License as published by |
365 | @@ -18,7 +15,6 @@ |
366 | */ |
367 | |
368 | import QtQuick 2.4 |
369 | -import Ubuntu.Settings.Menus 0.1 as Menus |
370 | import QMenuModel 0.1 |
371 | |
372 | QtObject { |
373 | @@ -47,6 +43,10 @@ |
374 | function activate() { |
375 | activated(); |
376 | } |
377 | + |
378 | + function updateState(newState) { |
379 | + state = newState; |
380 | + } |
381 | }", actionGroup); |
382 | } |
383 | } |
384 | |
385 | === modified file 'tests/mocks/Unity/Application/MirSurface.cpp' |
386 | --- tests/mocks/Unity/Application/MirSurface.cpp 2016-03-14 17:35:28 +0000 |
387 | +++ tests/mocks/Unity/Application/MirSurface.cpp 2016-03-14 17:35:29 +0000 |
388 | @@ -134,7 +134,6 @@ |
389 | Q_EMIT orientationAngleChanged(angle); |
390 | } |
391 | |
392 | - |
393 | Mir::ShellChrome MirSurface::shellChrome() const |
394 | { |
395 | return m_shellChrome; |
396 | @@ -149,7 +148,24 @@ |
397 | Q_EMIT shellChromeChanged(shellChrome); |
398 | } |
399 | |
400 | - |
401 | +QString MirSurface::keymapLayout() const |
402 | +{ |
403 | + return m_keyMap.first; |
404 | +} |
405 | + |
406 | +QString MirSurface::keymapVariant() const |
407 | +{ |
408 | + return m_keyMap.second; |
409 | +} |
410 | + |
411 | +void MirSurface::setKeymap(const QString &layout, const QString &variant) |
412 | +{ |
413 | + if (layout.isEmpty()) { |
414 | + return; |
415 | + } |
416 | + m_keyMap = qMakePair(layout, variant); |
417 | + Q_EMIT keymapChanged(layout, variant); |
418 | +} |
419 | |
420 | void MirSurface::registerView(qintptr viewId) |
421 | { |
422 | |
423 | === modified file 'tests/mocks/Unity/Application/MirSurface.h' |
424 | --- tests/mocks/Unity/Application/MirSurface.h 2016-03-14 17:35:28 +0000 |
425 | +++ tests/mocks/Unity/Application/MirSurface.h 2016-03-14 17:35:29 +0000 |
426 | @@ -75,6 +75,9 @@ |
427 | int heightIncrement() const override { return m_heightIncrement; } |
428 | |
429 | Mir::ShellChrome shellChrome() const override; |
430 | + QString keymapLayout() const override; |
431 | + QString keymapVariant() const override; |
432 | + Q_INVOKABLE void setKeymap(const QString &layout, const QString &variant) override; |
433 | |
434 | //// |
435 | // API for tests |
436 | @@ -158,6 +161,8 @@ |
437 | bool visible; |
438 | }; |
439 | QHash<qintptr, View> m_views; |
440 | + |
441 | + QPair<QString,QString> m_keyMap; // pair of layout+variant |
442 | }; |
443 | |
444 | #endif // MOCK_MIR_SURFACE_H |
445 | |
446 | === modified file 'tests/plugins/AccountsService/PropertiesServer.cpp' |
447 | --- tests/plugins/AccountsService/PropertiesServer.cpp 2016-03-10 22:42:54 +0000 |
448 | +++ tests/plugins/AccountsService/PropertiesServer.cpp 2016-03-14 17:35:29 +0000 |
449 | @@ -25,10 +25,16 @@ |
450 | #include <QDBusMessage> |
451 | #include <QDBusMetaType> |
452 | |
453 | +using StringMap = QMap<QString,QString>; |
454 | +using StringMapList = QList<StringMap>; |
455 | +Q_DECLARE_METATYPE(StringMapList) |
456 | + |
457 | PropertiesServer::PropertiesServer(QObject *parent) |
458 | : QObject(parent) |
459 | { |
460 | qDBusRegisterMetaType<QList<QVariantMap>>(); |
461 | + qDBusRegisterMetaType<StringMap>(); |
462 | + qDBusRegisterMetaType<StringMapList>(); |
463 | Reset(); |
464 | } |
465 | |
466 | @@ -63,6 +69,8 @@ |
467 | if (interface == QStringLiteral("com.canonical.unity.AccountsService") && |
468 | property == QStringLiteral("LauncherItems")) { |
469 | newValue = QVariant::fromValue(qdbus_cast<QList<QVariantMap>>(newValue.value<QDBusArgument>())); |
470 | + } else if (interface == "org.freedesktop.Accounts.User" && property == "InputSources") { |
471 | + newValue = QVariant::fromValue(qdbus_cast<StringMapList>(newValue.value<QDBusArgument>())); |
472 | } |
473 | |
474 | oldValue = newValue; |
475 | @@ -100,4 +108,5 @@ |
476 | m_properties["com.ubuntu.location.providers.here.AccountsService"]["LicenseBasePath"] = ""; |
477 | m_properties["org.freedesktop.Accounts.User"]["BackgroundFile"] = ""; |
478 | m_properties["org.freedesktop.Accounts.User"]["RealName"] = ""; |
479 | + m_properties["org.freedesktop.Accounts.User"]["InputSources"] = QVariant::fromValue(StringMapList()); |
480 | } |
481 | |
482 | === modified file 'tests/plugins/AccountsService/PropertiesServer.h' |
483 | --- tests/plugins/AccountsService/PropertiesServer.h 2016-02-09 13:58:35 +0000 |
484 | +++ tests/plugins/AccountsService/PropertiesServer.h 2016-03-14 17:35:29 +0000 |
485 | @@ -20,7 +20,6 @@ |
486 | #ifndef UNITY_PROPERTIESSERVER_H |
487 | #define UNITY_PROPERTIESSERVER_H |
488 | |
489 | -#include "PropertiesServer.h" |
490 | #include <QDBusContext> |
491 | #include <QDBusVariant> |
492 | #include <QObject> |
493 | |
494 | === modified file 'tests/plugins/AccountsService/client.cpp' |
495 | --- tests/plugins/AccountsService/client.cpp 2016-02-25 10:57:17 +0000 |
496 | +++ tests/plugins/AccountsService/client.cpp 2016-03-14 17:35:29 +0000 |
497 | @@ -23,6 +23,11 @@ |
498 | #include <QTest> |
499 | #include <QDebug> |
500 | #include <QDBusReply> |
501 | +#include <QDBusMetaType> |
502 | + |
503 | +using StringMap = QMap<QString,QString>; |
504 | +using StringMapList = QList<StringMap>; |
505 | +Q_DECLARE_METATYPE(StringMapList) |
506 | |
507 | template <class T> |
508 | QVariant dbusVariant(const T& value) { return QVariant::fromValue(QDBusVariant(value)); } |
509 | @@ -52,6 +57,9 @@ |
510 | |
511 | QObject::connect(m_uscInputInterface, SIGNAL(setMousePrimaryButtonCalled(int)), |
512 | this, SIGNAL(setMousePrimaryButtonCalled(int))); |
513 | + |
514 | + qDBusRegisterMetaType<StringMap>(); |
515 | + qDBusRegisterMetaType<StringMapList>(); |
516 | } |
517 | |
518 | private Q_SLOTS: |
519 | @@ -266,6 +274,28 @@ |
520 | QCOMPARE(arguments.at(0).toInt(), 0); |
521 | } |
522 | |
523 | + void testAsynchronousChangeForKeymaps() |
524 | + { |
525 | + AccountsService session(this, QTest::currentTestFunction()); |
526 | + |
527 | + QCOMPARE(session.keymaps(), {"us"}); |
528 | + |
529 | + StringMapList inputSources; |
530 | + StringMap map1; |
531 | + map1.insert("xkb", "cz+qwerty"); |
532 | + inputSources.append(map1); |
533 | + StringMap map2; |
534 | + map2.insert("xkb", "fr"); |
535 | + inputSources.append(map2); |
536 | + |
537 | + ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set", |
538 | + "org.freedesktop.Accounts.User", |
539 | + "InputSources", |
540 | + QVariant::fromValue(QDBusVariant(QVariant::fromValue(inputSources))))); |
541 | + QStringList result = {"cz+qwerty", "fr"}; |
542 | + QTRY_COMPARE(session.keymaps(), result); |
543 | + } |
544 | + |
545 | Q_SIGNALS: |
546 | void propertiesChanged(const QString &interface, const QVariantMap &changed, const QStringList &invalid); |
547 | void setMousePrimaryButtonCalled(int button); |
548 | |
549 | === modified file 'tests/qmltests/Stages/tst_DesktopStage.qml' |
550 | --- tests/qmltests/Stages/tst_DesktopStage.qml 2016-02-15 17:18:15 +0000 |
551 | +++ tests/qmltests/Stages/tst_DesktopStage.qml 2016-03-14 17:35:29 +0000 |
552 | @@ -510,8 +510,6 @@ |
553 | } |
554 | |
555 | function test_dropShadow() { |
556 | - killAllRunningApps(); |
557 | - |
558 | // verify the drop shadow is not visible initially |
559 | verify(PanelState.dropShadow == false); |
560 | |
561 | |
562 | === modified file 'tests/qmltests/tst_Shell.qml' |
563 | --- tests/qmltests/tst_Shell.qml 2016-03-14 17:35:28 +0000 |
564 | +++ tests/qmltests/tst_Shell.qml 2016-03-14 17:35:29 +0000 |
565 | @@ -2274,5 +2274,69 @@ |
566 | compare(topmostSurfaceItem.touchPressCount, 2); |
567 | compare(topmostSurfaceItem.touchReleaseCount, 2); |
568 | } |
569 | + |
570 | + function test_switchKeymap() { |
571 | + // start with phone shell |
572 | + loadShell("phone"); |
573 | + shell.usageScenario = "shell"; |
574 | + waitForRendering(shell); |
575 | + swipeAwayGreeter(); |
576 | + |
577 | + // configure keymaps |
578 | + AccountsService.keymaps = ["sk", "cz+qwerty", "fr"] // "configure" the keymaps for user |
579 | + |
580 | + // start some app |
581 | + var app = ApplicationManager.startApplication("dialer-app"); |
582 | + waitUntilAppWindowIsFullyLoaded(app); |
583 | + |
584 | + // verify the initial keymap of the newly started app is the first one from the list |
585 | + tryCompare(app.session.lastSurface, "keymapLayout", "sk"); |
586 | + tryCompare(app.session.lastSurface, "keymapVariant", ""); |
587 | + |
588 | + // switch to next keymap, should go to "cz+qwerty" |
589 | + keyClick(Qt.Key_Space, Qt.MetaModifier); |
590 | + tryCompare(app.session.lastSurface, "keymapLayout", "cz"); |
591 | + tryCompare(app.session.lastSurface, "keymapVariant", "qwerty"); |
592 | + |
593 | + // switch to next keymap, should go to "fr" |
594 | + keyClick(Qt.Key_Space, Qt.MetaModifier); |
595 | + |
596 | + // go to e.g. desktop stage |
597 | + loadShell("desktop"); |
598 | + shell.usageScenario = "desktop"; |
599 | + waitForRendering(shell); |
600 | + |
601 | + // start a second app, should get the last configured keyboard, "fr" |
602 | + var app2 = ApplicationManager.startApplication("calendar-app"); |
603 | + waitUntilAppWindowIsFullyLoaded(app2); |
604 | + tryCompare(app2.session.lastSurface, "keymapLayout", "fr"); |
605 | + tryCompare(app2.session.lastSurface, "keymapVariant", ""); |
606 | + |
607 | + // focus our first app, make sure it also has the "fr" keymap |
608 | + ApplicationManager.requestFocusApplication("dialer-app"); |
609 | + tryCompare(app.session.lastSurface, "keymapLayout", "fr"); |
610 | + tryCompare(app.session.lastSurface, "keymapVariant", ""); |
611 | + |
612 | + // switch to previous keymap, should be "cz+qwerty" |
613 | + keyClick(Qt.Key_Space, Qt.MetaModifier|Qt.ShiftModifier); |
614 | + tryCompare(app.session.lastSurface, "keymapLayout", "cz"); |
615 | + tryCompare(app.session.lastSurface, "keymapVariant", "qwerty"); |
616 | + |
617 | + // go next twice to "sk", past the end |
618 | + keyClick(Qt.Key_Space, Qt.MetaModifier); |
619 | + keyClick(Qt.Key_Space, Qt.MetaModifier); |
620 | + tryCompare(app.session.lastSurface, "keymapLayout", "sk"); |
621 | + tryCompare(app.session.lastSurface, "keymapVariant", ""); |
622 | + |
623 | + // go back once to past the beginning, to "fr" |
624 | + keyClick(Qt.Key_Space, Qt.MetaModifier|Qt.ShiftModifier); |
625 | + tryCompare(app.session.lastSurface, "keymapLayout", "fr"); |
626 | + tryCompare(app.session.lastSurface, "keymapVariant", ""); |
627 | + |
628 | + // switch to app2, should also get "fr" |
629 | + ApplicationManager.requestFocusApplication("calendar-app"); |
630 | + tryCompare(app2.session.lastSurface, "keymapLayout", "fr"); |
631 | + tryCompare(app2.session.lastSurface, "keymapVariant", ""); |
632 | + } |
633 | } |
634 | } |
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:/