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

Proposed by Michał Sawicz
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
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

To post a comment you must log in.
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
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.

review: Needs Fixing
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

FAILED: Continuous integration, rev:2278
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/695/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/914
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/931
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/931
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/929
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/929/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial/929
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial/929/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/929
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/929/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial/929
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial/929/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/929
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/929/artifact/output/*zip*/output.zip
    FAILURE: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial/929/console

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/695/rebuild

review: Needs Fixing (continuous-integration)
2279. By Lukáš Tinkl

refine test for switching keymaps, move to tst_Shell

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 :

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

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
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

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

PASSED: Continuous integration, rev:2281
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/738/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=qmluitests.sh/414
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial,testname=qmluitests.sh/414
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=phone-armhf,release=vivid+overlay,testname=autopilot.sh/414
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/967
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/984
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/984
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/982
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/982/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial/982
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial/982/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/982
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/982/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial/982
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial/982/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/982
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/982/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial/982
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial/982/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/738/rebuild

review: Approve (continuous-integration)
Revision history for this message
Michael Terry (mterry) wrote :

Note to self:

sudo gdbus call --system --dest org.freedesktop.Accounts --object-path /org/freedesktop/Accounts/User32011 --method org.freedesktop.Accounts.User.SetInputSources '[{"xkb": "us"}, {"xkb": "zh"}]'

Revision history for this message
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!

review: Approve
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

"""
    function switchToKeymap(keymap) {
        var finalKeymap = keymap.split("+");
        savedKeymap = keymap; // save the keymap in case the surface changes later

        if (surface) {
            surface.setKeymap(finalKeymap[0], finalKeymap[1] || "");
        }
    }
"""

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
    }
}
"""

review: Needs Information
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

And as a continuation of the idea in my previous comment, in ApplicationWindow:

"""
function switchToKeymap(keymap) {
        sessionContainer.surfaceContainer.switchToKeymap(keymap);
    }
"""

Would be something like:

"""
property alias keymap: sessionContainer.surfaceContainer.keymap
"""

Revision history for this message
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).

Revision history for this message
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'plugins/AccountsService/AccountsService.cpp'
--- plugins/AccountsService/AccountsService.cpp 2016-03-14 17:35:28 +0000
+++ plugins/AccountsService/AccountsService.cpp 2016-03-14 17:35:29 +0000
@@ -36,6 +36,7 @@
36#define PROP_ENABLE_INDICATORS_WHILE_LOCKED QStringLiteral("EnableIndicatorsWhileLocked")36#define PROP_ENABLE_INDICATORS_WHILE_LOCKED QStringLiteral("EnableIndicatorsWhileLocked")
37#define PROP_ENABLE_LAUNCHER_WHILE_LOCKED QStringLiteral("EnableLauncherWhileLocked")37#define PROP_ENABLE_LAUNCHER_WHILE_LOCKED QStringLiteral("EnableLauncherWhileLocked")
38#define PROP_FAILED_LOGINS QStringLiteral("FailedLogins")38#define PROP_FAILED_LOGINS QStringLiteral("FailedLogins")
39#define PROP_INPUT_SOURCES QStringLiteral("InputSources")
39#define PROP_LICENSE_ACCEPTED QStringLiteral("LicenseAccepted")40#define PROP_LICENSE_ACCEPTED QStringLiteral("LicenseAccepted")
40#define PROP_LICENSE_BASE_PATH QStringLiteral("LicenseBasePath")41#define PROP_LICENSE_BASE_PATH QStringLiteral("LicenseBasePath")
41#define PROP_MOUSE_CURSOR_SPEED QStringLiteral("MouseCursorSpeed")42#define PROP_MOUSE_CURSOR_SPEED QStringLiteral("MouseCursorSpeed")
@@ -54,6 +55,10 @@
54#define PROP_TOUCHPAD_TAP_TO_CLICK QStringLiteral("TouchpadTapToClick")55#define PROP_TOUCHPAD_TAP_TO_CLICK QStringLiteral("TouchpadTapToClick")
55#define PROP_TOUCHPAD_TWO_FINGER_SCROLL QStringLiteral("TouchpadTwoFingerScroll")56#define PROP_TOUCHPAD_TWO_FINGER_SCROLL QStringLiteral("TouchpadTwoFingerScroll")
5657
58using StringMap = QMap<QString,QString>;
59using StringMapList = QList<StringMap>;
60Q_DECLARE_METATYPE(StringMapList)
61
5762
58QVariant primaryButtonConverter(const QVariant &value)63QVariant primaryButtonConverter(const QVariant &value)
59{64{
@@ -82,6 +87,7 @@
82 registerProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE, QStringLiteral("backgroundFileChanged"));87 registerProperty(IFACE_ACCOUNTS_USER, PROP_BACKGROUND_FILE, QStringLiteral("backgroundFileChanged"));
83 registerProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, QStringLiteral("emailChanged"));88 registerProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, QStringLiteral("emailChanged"));
84 registerProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, QStringLiteral("realNameChanged"));89 registerProperty(IFACE_ACCOUNTS_USER, PROP_REAL_NAME, QStringLiteral("realNameChanged"));
90 registerProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES, QStringLiteral("keymapsChanged"));
85 registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, QStringLiteral("hereEnabledChanged"));91 registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_ACCEPTED, QStringLiteral("hereEnabledChanged"));
86 registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH, QStringLiteral("hereLicensePathChanged"));92 registerProperty(IFACE_LOCATION_HERE, PROP_LICENSE_BASE_PATH, QStringLiteral("hereLicensePathChanged"));
87 registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED, QStringLiteral("enableLauncherWhileLockedChanged"));93 registerProperty(IFACE_UBUNTU_SECURITY, PROP_ENABLE_LAUNCHER_WHILE_LOCKED, QStringLiteral("enableLauncherWhileLockedChanged"));
@@ -230,6 +236,26 @@
230 setProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, email);236 setProperty(IFACE_ACCOUNTS_USER, PROP_EMAIL, email);
231}237}
232238
239QStringList AccountsService::keymaps() const
240{
241 auto value = getProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES);
242 QDBusArgument arg = value.value<QDBusArgument>();
243 StringMapList maps = qdbus_cast<StringMapList>(arg);
244 QStringList simplifiedMaps;
245
246 Q_FOREACH(const StringMap &map, maps) {
247 Q_FOREACH(const QString &entry, map) {
248 simplifiedMaps.append(entry);
249 }
250 }
251
252 if (!simplifiedMaps.isEmpty()) {
253 return simplifiedMaps;
254 }
255
256 return {QStringLiteral("us")};
257}
258
233uint AccountsService::failedLogins() const259uint AccountsService::failedLogins() const
234{260{
235 return getProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS).toUInt();261 return getProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_LOGINS).toUInt();
236262
=== modified file 'plugins/AccountsService/AccountsService.h'
--- plugins/AccountsService/AccountsService.h 2016-03-14 17:35:28 +0000
+++ plugins/AccountsService/AccountsService.h 2016-03-14 17:35:29 +0000
@@ -20,6 +20,7 @@
20#include <QHash>20#include <QHash>
21#include <QObject>21#include <QObject>
22#include <QString>22#include <QString>
23#include <QStringList>
23#include <QVariant>24#include <QVariant>
2425
25class AccountsServiceDBusAdaptor;26class AccountsServiceDBusAdaptor;
@@ -68,6 +69,9 @@
68 NOTIFY hereLicensePathChanged)69 NOTIFY hereLicensePathChanged)
69 Q_PROPERTY(QString realName READ realName WRITE setRealName NOTIFY realNameChanged)70 Q_PROPERTY(QString realName READ realName WRITE setRealName NOTIFY realNameChanged)
70 Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY emailChanged)71 Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY emailChanged)
72 Q_PROPERTY(QStringList keymaps
73 READ keymaps
74 NOTIFY keymapsChanged)
7175
72public:76public:
73 enum PasswordDisplayHint {77 enum PasswordDisplayHint {
@@ -97,6 +101,7 @@
97 void setRealName(const QString &realName);101 void setRealName(const QString &realName);
98 QString email() const;102 QString email() const;
99 void setEmail(const QString &email);103 void setEmail(const QString &email);
104 QStringList keymaps() const;
100105
101Q_SIGNALS:106Q_SIGNALS:
102 void userChanged();107 void userChanged();
@@ -111,6 +116,7 @@
111 void hereLicensePathChanged();116 void hereLicensePathChanged();
112 void realNameChanged();117 void realNameChanged();
113 void emailChanged();118 void emailChanged();
119 void keymapsChanged();
114120
115private Q_SLOTS:121private Q_SLOTS:
116 void onPropertiesChanged(const QString &user, const QString &interface, const QStringList &changed);122 void onPropertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
117123
=== modified file 'qml/Shell.qml'
--- qml/Shell.qml 2016-03-14 17:35:28 +0000
+++ qml/Shell.qml 2016-03-14 17:35:29 +0000
@@ -102,6 +102,8 @@
102 // internal props from here onwards102 // internal props from here onwards
103 readonly property var mainApp:103 readonly property var mainApp:
104 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null104 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null
105 readonly property var mainAppWindow:
106 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindow : null
105107
106 // Disable everything while greeter is waiting, so that the user can't swipe108 // Disable everything while greeter is waiting, so that the user can't swipe
107 // the greeter or launcher until we know whether the session is locked.109 // the greeter or launcher until we know whether the session is locked.
@@ -736,6 +738,54 @@
736 onMouseMoved: { cursor.opacity = 1; }738 onMouseMoved: { cursor.opacity = 1; }
737 }739 }
738740
741 // keymap switching
742 GlobalShortcut {
743 shortcut: Qt.MetaModifier|Qt.Key_Space
744 onTriggered: keymapPriv.nextKeymap()
745 active: keymapPriv.keymapCount > 1
746 }
747
748 GlobalShortcut {
749 shortcut: Qt.MetaModifier|Qt.ShiftModifier|Qt.Key_Space
750 onTriggered: keymapPriv.previousKeymap()
751 active: keymapPriv.keymapCount > 1
752 }
753
754 QtObject {
755 id: keymapPriv
756
757 readonly property var keymaps: AccountsService.keymaps
758 readonly property int keymapCount: keymaps.length
759 property int currentKeymapIndex: 0 // the new one that we're setting
760 onCurrentKeymapIndexChanged: switchToKeymap();
761
762 function nextKeymap() {
763 var nextIndex = 0;
764
765 if (currentKeymapIndex !== -1 && currentKeymapIndex < keymapCount - 1) {
766 nextIndex = currentKeymapIndex + 1;
767 }
768 currentKeymapIndex = nextIndex;
769 }
770
771 function previousKeymap() {
772 var prevIndex = keymapCount - 1;
773
774 if (currentKeymapIndex > 0) {
775 prevIndex = currentKeymapIndex - 1;
776 }
777 currentKeymapIndex = prevIndex;
778 }
779
780 function switchToKeymap() {
781 if (mainAppWindow) {
782 mainAppWindow.switchToKeymap(keymaps[currentKeymapIndex]);
783 }
784 }
785 }
786
787 onMainAppWindowChanged: keymapPriv.switchToKeymap()
788
739 Rectangle {789 Rectangle {
740 id: shutdownFadeOutRectangle790 id: shutdownFadeOutRectangle
741 z: cursor.z + 1791 z: cursor.z + 1
742792
=== modified file 'qml/Stages/AbstractStage.qml'
--- qml/Stages/AbstractStage.qml 2016-03-14 17:35:28 +0000
+++ qml/Stages/AbstractStage.qml 2016-03-14 17:35:29 +0000
@@ -44,6 +44,7 @@
4444
45 // To be read from outside45 // To be read from outside
46 property var mainApp: null46 property var mainApp: null
47 property var mainAppWindow: null
47 property int mainAppWindowOrientationAngle: 048 property int mainAppWindowOrientationAngle: 0
48 property bool orientationChangesEnabled49 property bool orientationChangesEnabled
49 property int supportedOrientations: Qt.PortraitOrientation50 property int supportedOrientations: Qt.PortraitOrientation
5051
=== modified file 'qml/Stages/ApplicationWindow.qml'
--- qml/Stages/ApplicationWindow.qml 2016-03-14 17:35:28 +0000
+++ qml/Stages/ApplicationWindow.qml 2016-03-14 17:35:29 +0000
@@ -39,6 +39,10 @@
39 property int requestedWidth: -139 property int requestedWidth: -1
40 property int requestedHeight: -140 property int requestedHeight: -1
4141
42 function switchToKeymap(keymap) {
43 sessionContainer.surfaceContainer.switchToKeymap(keymap);
44 }
45
42 readonly property int minimumWidth: sessionContainer.surface ? sessionContainer.surface.minimumWidth : 046 readonly property int minimumWidth: sessionContainer.surface ? sessionContainer.surface.minimumWidth : 0
43 readonly property int minimumHeight: sessionContainer.surface ? sessionContainer.surface.minimumHeight : 047 readonly property int minimumHeight: sessionContainer.surface ? sessionContainer.surface.minimumHeight : 0
44 readonly property int maximumWidth: sessionContainer.surface ? sessionContainer.surface.maximumWidth : 048 readonly property int maximumWidth: sessionContainer.surface ? sessionContainer.surface.maximumWidth : 0
4549
=== modified file 'qml/Stages/DesktopStage.qml'
--- qml/Stages/DesktopStage.qml 2016-03-14 17:35:28 +0000
+++ qml/Stages/DesktopStage.qml 2016-03-14 17:35:29 +0000
@@ -40,6 +40,8 @@
40 ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)40 ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
41 : null41 : null
4242
43 mainAppWindow: priv.focusedAppDelegate ? priv.focusedAppDelegate.appWindow : null
44
43 // application windows never rotate independently45 // application windows never rotate independently
44 mainAppWindowOrientationAngle: shellOrientationAngle46 mainAppWindowOrientationAngle: shellOrientationAngle
4547
@@ -284,6 +286,8 @@
284 property bool visuallyMaximized: false286 property bool visuallyMaximized: false
285 property bool visuallyMinimized: false287 property bool visuallyMinimized: false
286288
289 readonly property alias appWindow: decoratedWindow.window
290
287 onFocusChanged: {291 onFocusChanged: {
288 if (focus && ApplicationManager.focusedApplicationId !== appId) {292 if (focus && ApplicationManager.focusedApplicationId !== appId) {
289 ApplicationManager.focusApplication(appId);293 ApplicationManager.focusApplication(appId);
290294
=== modified file 'qml/Stages/PhoneStage.qml'
--- qml/Stages/PhoneStage.qml 2016-03-14 17:35:28 +0000
+++ qml/Stages/PhoneStage.qml 2016-03-14 17:35:29 +0000
@@ -111,6 +111,8 @@
111 ? applicationManager.findApplication(applicationManager.focusedApplicationId)111 ? applicationManager.findApplication(applicationManager.focusedApplicationId)
112 : null112 : null
113113
114 mainAppWindow: priv.focusedAppDelegate ? priv.focusedAppDelegate.appWindow : null
115
114 orientationChangesEnabled: priv.focusedAppOrientationChangesEnabled116 orientationChangesEnabled: priv.focusedAppOrientationChangesEnabled
115 && !priv.focusedAppDelegateIsDislocated117 && !priv.focusedAppDelegateIsDislocated
116 && !(priv.focusedAppDelegate && priv.focusedAppDelegate.xBehavior.running)118 && !(priv.focusedAppDelegate && priv.focusedAppDelegate.xBehavior.running)
117119
=== modified file 'qml/Stages/SpreadDelegate.qml'
--- qml/Stages/SpreadDelegate.qml 2016-03-14 17:35:28 +0000
+++ qml/Stages/SpreadDelegate.qml 2016-03-14 17:35:29 +0000
@@ -37,6 +37,7 @@
37 | Qt.LandscapeOrientation37 | Qt.LandscapeOrientation
38 | Qt.InvertedPortraitOrientation38 | Qt.InvertedPortraitOrientation
39 | Qt.InvertedLandscapeOrientation39 | Qt.InvertedLandscapeOrientation
40 readonly property alias appWindow: appWindow
4041
41 // to be set from outside42 // to be set from outside
42 property bool interactive: true43 property bool interactive: true
4344
=== modified file 'qml/Stages/SurfaceContainer.qml'
--- qml/Stages/SurfaceContainer.qml 2016-03-14 17:35:28 +0000
+++ qml/Stages/SurfaceContainer.qml 2016-03-14 17:35:29 +0000
@@ -19,6 +19,7 @@
19import Ubuntu.Gestures 0.1 // For TouchGate19import Ubuntu.Gestures 0.1 // For TouchGate
20import Utils 0.1 // for InputWatcher20import Utils 0.1 // for InputWatcher
21import Unity.Application 0.1 // for MirSurfaceItem21import Unity.Application 0.1 // for MirSurfaceItem
22import AccountsService 0.1
2223
23FocusScope {24FocusScope {
24 id: root25 id: root
@@ -34,10 +35,22 @@
34 property int requestedWidth: -135 property int requestedWidth: -1
35 property int requestedHeight: -136 property int requestedHeight: -1
3637
38 property string savedKeymap: AccountsService.keymaps[0] // start with the user default
39
37 onSurfaceChanged: {40 onSurfaceChanged: {
38 if (surface) {41 if (surface) {
39 surfaceItem.surface = surface;42 surfaceItem.surface = surface;
40 root.hadSurface = false;43 root.hadSurface = false;
44 switchToKeymap(savedKeymap);
45 }
46 }
47
48 function switchToKeymap(keymap) {
49 var finalKeymap = keymap.split("+");
50 savedKeymap = keymap; // save the keymap in case the surface changes later
51
52 if (surface) {
53 surface.setKeymap(finalKeymap[0], finalKeymap[1] || "");
41 }54 }
42 }55 }
4356
4457
=== modified file 'qml/Stages/TabletStage.qml'
--- qml/Stages/TabletStage.qml 2016-03-14 17:35:28 +0000
+++ qml/Stages/TabletStage.qml 2016-03-14 17:35:29 +0000
@@ -76,6 +76,8 @@
76 }76 }
77 }77 }
7878
79 mainAppWindow: priv.focusedAppDelegate ? priv.focusedAppDelegate.appWindow : null
80
79 orientationChangesEnabled: priv.mainAppOrientationChangesEnabled81 orientationChangesEnabled: priv.mainAppOrientationChangesEnabled
8082
81 supportedOrientations: {83 supportedOrientations: {
8284
=== modified file 'tests/mocks/AccountsService/AccountsService.cpp'
--- tests/mocks/AccountsService/AccountsService.cpp 2016-03-14 17:35:28 +0000
+++ tests/mocks/AccountsService/AccountsService.cpp 2016-03-14 17:35:29 +0000
@@ -172,3 +172,20 @@
172 m_email = email;172 m_email = email;
173 Q_EMIT emailChanged();173 Q_EMIT emailChanged();
174}174}
175
176QStringList AccountsService::keymaps() const
177{
178 if (!m_kbdMap.isEmpty()) {
179 return m_kbdMap;
180 }
181
182 return {QStringLiteral("us")};
183}
184
185void AccountsService::setKeymaps(const QStringList &keymaps)
186{
187 if (keymaps != m_kbdMap) {
188 m_kbdMap = keymaps;
189 Q_EMIT keymapsChanged();
190 }
191}
175192
=== modified file 'tests/mocks/AccountsService/AccountsService.h'
--- tests/mocks/AccountsService/AccountsService.h 2016-03-14 17:35:28 +0000
+++ tests/mocks/AccountsService/AccountsService.h 2016-03-14 17:35:29 +0000
@@ -69,6 +69,10 @@
69 NOTIFY hereLicensePathChanged)69 NOTIFY hereLicensePathChanged)
70 Q_PROPERTY(QString realName READ realName WRITE setRealName NOTIFY realNameChanged)70 Q_PROPERTY(QString realName READ realName WRITE setRealName NOTIFY realNameChanged)
71 Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY emailChanged)71 Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY emailChanged)
72 Q_PROPERTY(QStringList keymaps
73 READ keymaps
74 WRITE setKeymaps // only in mock
75 NOTIFY keymapsChanged)
7276
73public:77public:
74 enum PasswordDisplayHint {78 enum PasswordDisplayHint {
@@ -102,6 +106,8 @@
102 void setRealName(const QString &realName);106 void setRealName(const QString &realName);
103 QString email() const;107 QString email() const;
104 void setEmail(const QString &email);108 void setEmail(const QString &email);
109 QStringList keymaps() const;
110 void setKeymaps(const QStringList &keymaps);
105111
106Q_SIGNALS:112Q_SIGNALS:
107 void userChanged();113 void userChanged();
@@ -116,6 +122,7 @@
116 void hereLicensePathChanged();122 void hereLicensePathChanged();
117 void realNameChanged();123 void realNameChanged();
118 void emailChanged();124 void emailChanged();
125 void keymapsChanged();
119126
120private:127private:
121 bool m_enableLauncherWhileLocked;128 bool m_enableLauncherWhileLocked;
@@ -128,6 +135,7 @@
128 bool m_hereEnabled;135 bool m_hereEnabled;
129 QString m_hereLicensePath;136 QString m_hereLicensePath;
130 QString m_realName;137 QString m_realName;
138 QStringList m_kbdMap;
131 QString m_email;139 QString m_email;
132};140};
133141
134142
=== modified file 'tests/mocks/QMenuModel/QDBusActionGroup.qml'
--- tests/mocks/QMenuModel/QDBusActionGroup.qml 2015-07-21 14:38:35 +0000
+++ tests/mocks/QMenuModel/QDBusActionGroup.qml 2016-03-14 17:35:29 +0000
@@ -1,8 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *
4 * Authors:
5 * Daniel d'Andrada <daniel.dandrada@canonical.com>
6 *3 *
7 * This program is free software; you can redistribute it and/or modify4 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by5 * it under the terms of the GNU General Public License as published by
@@ -18,7 +15,6 @@
18 */15 */
1916
20import QtQuick 2.417import QtQuick 2.4
21import Ubuntu.Settings.Menus 0.1 as Menus
22import QMenuModel 0.118import QMenuModel 0.1
2319
24QtObject {20QtObject {
@@ -47,6 +43,10 @@
47 function activate() {43 function activate() {
48 activated();44 activated();
49 }45 }
46
47 function updateState(newState) {
48 state = newState;
49 }
50 }", actionGroup);50 }", actionGroup);
51 }51 }
52}52}
5353
=== modified file 'tests/mocks/Unity/Application/MirSurface.cpp'
--- tests/mocks/Unity/Application/MirSurface.cpp 2016-03-14 17:35:28 +0000
+++ tests/mocks/Unity/Application/MirSurface.cpp 2016-03-14 17:35:29 +0000
@@ -134,7 +134,6 @@
134 Q_EMIT orientationAngleChanged(angle);134 Q_EMIT orientationAngleChanged(angle);
135}135}
136136
137
138Mir::ShellChrome MirSurface::shellChrome() const137Mir::ShellChrome MirSurface::shellChrome() const
139{138{
140 return m_shellChrome;139 return m_shellChrome;
@@ -149,7 +148,24 @@
149 Q_EMIT shellChromeChanged(shellChrome);148 Q_EMIT shellChromeChanged(shellChrome);
150}149}
151150
152151QString MirSurface::keymapLayout() const
152{
153 return m_keyMap.first;
154}
155
156QString MirSurface::keymapVariant() const
157{
158 return m_keyMap.second;
159}
160
161void MirSurface::setKeymap(const QString &layout, const QString &variant)
162{
163 if (layout.isEmpty()) {
164 return;
165 }
166 m_keyMap = qMakePair(layout, variant);
167 Q_EMIT keymapChanged(layout, variant);
168}
153169
154void MirSurface::registerView(qintptr viewId)170void MirSurface::registerView(qintptr viewId)
155{171{
156172
=== modified file 'tests/mocks/Unity/Application/MirSurface.h'
--- tests/mocks/Unity/Application/MirSurface.h 2016-03-14 17:35:28 +0000
+++ tests/mocks/Unity/Application/MirSurface.h 2016-03-14 17:35:29 +0000
@@ -75,6 +75,9 @@
75 int heightIncrement() const override { return m_heightIncrement; }75 int heightIncrement() const override { return m_heightIncrement; }
7676
77 Mir::ShellChrome shellChrome() const override;77 Mir::ShellChrome shellChrome() const override;
78 QString keymapLayout() const override;
79 QString keymapVariant() const override;
80 Q_INVOKABLE void setKeymap(const QString &layout, const QString &variant) override;
7881
79 ////82 ////
80 // API for tests83 // API for tests
@@ -158,6 +161,8 @@
158 bool visible;161 bool visible;
159 };162 };
160 QHash<qintptr, View> m_views;163 QHash<qintptr, View> m_views;
164
165 QPair<QString,QString> m_keyMap; // pair of layout+variant
161};166};
162167
163#endif // MOCK_MIR_SURFACE_H168#endif // MOCK_MIR_SURFACE_H
164169
=== modified file 'tests/plugins/AccountsService/PropertiesServer.cpp'
--- tests/plugins/AccountsService/PropertiesServer.cpp 2016-03-10 22:42:54 +0000
+++ tests/plugins/AccountsService/PropertiesServer.cpp 2016-03-14 17:35:29 +0000
@@ -25,10 +25,16 @@
25#include <QDBusMessage>25#include <QDBusMessage>
26#include <QDBusMetaType>26#include <QDBusMetaType>
2727
28using StringMap = QMap<QString,QString>;
29using StringMapList = QList<StringMap>;
30Q_DECLARE_METATYPE(StringMapList)
31
28PropertiesServer::PropertiesServer(QObject *parent)32PropertiesServer::PropertiesServer(QObject *parent)
29 : QObject(parent)33 : QObject(parent)
30{34{
31 qDBusRegisterMetaType<QList<QVariantMap>>();35 qDBusRegisterMetaType<QList<QVariantMap>>();
36 qDBusRegisterMetaType<StringMap>();
37 qDBusRegisterMetaType<StringMapList>();
32 Reset();38 Reset();
33}39}
3440
@@ -63,6 +69,8 @@
63 if (interface == QStringLiteral("com.canonical.unity.AccountsService") &&69 if (interface == QStringLiteral("com.canonical.unity.AccountsService") &&
64 property == QStringLiteral("LauncherItems")) {70 property == QStringLiteral("LauncherItems")) {
65 newValue = QVariant::fromValue(qdbus_cast<QList<QVariantMap>>(newValue.value<QDBusArgument>()));71 newValue = QVariant::fromValue(qdbus_cast<QList<QVariantMap>>(newValue.value<QDBusArgument>()));
72 } else if (interface == "org.freedesktop.Accounts.User" && property == "InputSources") {
73 newValue = QVariant::fromValue(qdbus_cast<StringMapList>(newValue.value<QDBusArgument>()));
66 }74 }
6775
68 oldValue = newValue;76 oldValue = newValue;
@@ -100,4 +108,5 @@
100 m_properties["com.ubuntu.location.providers.here.AccountsService"]["LicenseBasePath"] = "";108 m_properties["com.ubuntu.location.providers.here.AccountsService"]["LicenseBasePath"] = "";
101 m_properties["org.freedesktop.Accounts.User"]["BackgroundFile"] = "";109 m_properties["org.freedesktop.Accounts.User"]["BackgroundFile"] = "";
102 m_properties["org.freedesktop.Accounts.User"]["RealName"] = "";110 m_properties["org.freedesktop.Accounts.User"]["RealName"] = "";
111 m_properties["org.freedesktop.Accounts.User"]["InputSources"] = QVariant::fromValue(StringMapList());
103}112}
104113
=== modified file 'tests/plugins/AccountsService/PropertiesServer.h'
--- tests/plugins/AccountsService/PropertiesServer.h 2016-02-09 13:58:35 +0000
+++ tests/plugins/AccountsService/PropertiesServer.h 2016-03-14 17:35:29 +0000
@@ -20,7 +20,6 @@
20#ifndef UNITY_PROPERTIESSERVER_H20#ifndef UNITY_PROPERTIESSERVER_H
21#define UNITY_PROPERTIESSERVER_H21#define UNITY_PROPERTIESSERVER_H
2222
23#include "PropertiesServer.h"
24#include <QDBusContext>23#include <QDBusContext>
25#include <QDBusVariant>24#include <QDBusVariant>
26#include <QObject>25#include <QObject>
2726
=== modified file 'tests/plugins/AccountsService/client.cpp'
--- tests/plugins/AccountsService/client.cpp 2016-02-25 10:57:17 +0000
+++ tests/plugins/AccountsService/client.cpp 2016-03-14 17:35:29 +0000
@@ -23,6 +23,11 @@
23#include <QTest>23#include <QTest>
24#include <QDebug>24#include <QDebug>
25#include <QDBusReply>25#include <QDBusReply>
26#include <QDBusMetaType>
27
28using StringMap = QMap<QString,QString>;
29using StringMapList = QList<StringMap>;
30Q_DECLARE_METATYPE(StringMapList)
2631
27template <class T>32template <class T>
28QVariant dbusVariant(const T& value) { return QVariant::fromValue(QDBusVariant(value)); }33QVariant dbusVariant(const T& value) { return QVariant::fromValue(QDBusVariant(value)); }
@@ -52,6 +57,9 @@
5257
53 QObject::connect(m_uscInputInterface, SIGNAL(setMousePrimaryButtonCalled(int)),58 QObject::connect(m_uscInputInterface, SIGNAL(setMousePrimaryButtonCalled(int)),
54 this, SIGNAL(setMousePrimaryButtonCalled(int)));59 this, SIGNAL(setMousePrimaryButtonCalled(int)));
60
61 qDBusRegisterMetaType<StringMap>();
62 qDBusRegisterMetaType<StringMapList>();
55 }63 }
5664
57private Q_SLOTS:65private Q_SLOTS:
@@ -266,6 +274,28 @@
266 QCOMPARE(arguments.at(0).toInt(), 0);274 QCOMPARE(arguments.at(0).toInt(), 0);
267 }275 }
268276
277 void testAsynchronousChangeForKeymaps()
278 {
279 AccountsService session(this, QTest::currentTestFunction());
280
281 QCOMPARE(session.keymaps(), {"us"});
282
283 StringMapList inputSources;
284 StringMap map1;
285 map1.insert("xkb", "cz+qwerty");
286 inputSources.append(map1);
287 StringMap map2;
288 map2.insert("xkb", "fr");
289 inputSources.append(map2);
290
291 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
292 "org.freedesktop.Accounts.User",
293 "InputSources",
294 QVariant::fromValue(QDBusVariant(QVariant::fromValue(inputSources)))));
295 QStringList result = {"cz+qwerty", "fr"};
296 QTRY_COMPARE(session.keymaps(), result);
297 }
298
269Q_SIGNALS:299Q_SIGNALS:
270 void propertiesChanged(const QString &interface, const QVariantMap &changed, const QStringList &invalid);300 void propertiesChanged(const QString &interface, const QVariantMap &changed, const QStringList &invalid);
271 void setMousePrimaryButtonCalled(int button);301 void setMousePrimaryButtonCalled(int button);
272302
=== modified file 'tests/qmltests/Stages/tst_DesktopStage.qml'
--- tests/qmltests/Stages/tst_DesktopStage.qml 2016-02-15 17:18:15 +0000
+++ tests/qmltests/Stages/tst_DesktopStage.qml 2016-03-14 17:35:29 +0000
@@ -510,8 +510,6 @@
510 }510 }
511511
512 function test_dropShadow() {512 function test_dropShadow() {
513 killAllRunningApps();
514
515 // verify the drop shadow is not visible initially513 // verify the drop shadow is not visible initially
516 verify(PanelState.dropShadow == false);514 verify(PanelState.dropShadow == false);
517515
518516
=== modified file 'tests/qmltests/tst_Shell.qml'
--- tests/qmltests/tst_Shell.qml 2016-03-14 17:35:28 +0000
+++ tests/qmltests/tst_Shell.qml 2016-03-14 17:35:29 +0000
@@ -2274,5 +2274,69 @@
2274 compare(topmostSurfaceItem.touchPressCount, 2);2274 compare(topmostSurfaceItem.touchPressCount, 2);
2275 compare(topmostSurfaceItem.touchReleaseCount, 2);2275 compare(topmostSurfaceItem.touchReleaseCount, 2);
2276 }2276 }
2277
2278 function test_switchKeymap() {
2279 // start with phone shell
2280 loadShell("phone");
2281 shell.usageScenario = "shell";
2282 waitForRendering(shell);
2283 swipeAwayGreeter();
2284
2285 // configure keymaps
2286 AccountsService.keymaps = ["sk", "cz+qwerty", "fr"] // "configure" the keymaps for user
2287
2288 // start some app
2289 var app = ApplicationManager.startApplication("dialer-app");
2290 waitUntilAppWindowIsFullyLoaded(app);
2291
2292 // verify the initial keymap of the newly started app is the first one from the list
2293 tryCompare(app.session.lastSurface, "keymapLayout", "sk");
2294 tryCompare(app.session.lastSurface, "keymapVariant", "");
2295
2296 // switch to next keymap, should go to "cz+qwerty"
2297 keyClick(Qt.Key_Space, Qt.MetaModifier);
2298 tryCompare(app.session.lastSurface, "keymapLayout", "cz");
2299 tryCompare(app.session.lastSurface, "keymapVariant", "qwerty");
2300
2301 // switch to next keymap, should go to "fr"
2302 keyClick(Qt.Key_Space, Qt.MetaModifier);
2303
2304 // go to e.g. desktop stage
2305 loadShell("desktop");
2306 shell.usageScenario = "desktop";
2307 waitForRendering(shell);
2308
2309 // start a second app, should get the last configured keyboard, "fr"
2310 var app2 = ApplicationManager.startApplication("calendar-app");
2311 waitUntilAppWindowIsFullyLoaded(app2);
2312 tryCompare(app2.session.lastSurface, "keymapLayout", "fr");
2313 tryCompare(app2.session.lastSurface, "keymapVariant", "");
2314
2315 // focus our first app, make sure it also has the "fr" keymap
2316 ApplicationManager.requestFocusApplication("dialer-app");
2317 tryCompare(app.session.lastSurface, "keymapLayout", "fr");
2318 tryCompare(app.session.lastSurface, "keymapVariant", "");
2319
2320 // switch to previous keymap, should be "cz+qwerty"
2321 keyClick(Qt.Key_Space, Qt.MetaModifier|Qt.ShiftModifier);
2322 tryCompare(app.session.lastSurface, "keymapLayout", "cz");
2323 tryCompare(app.session.lastSurface, "keymapVariant", "qwerty");
2324
2325 // go next twice to "sk", past the end
2326 keyClick(Qt.Key_Space, Qt.MetaModifier);
2327 keyClick(Qt.Key_Space, Qt.MetaModifier);
2328 tryCompare(app.session.lastSurface, "keymapLayout", "sk");
2329 tryCompare(app.session.lastSurface, "keymapVariant", "");
2330
2331 // go back once to past the beginning, to "fr"
2332 keyClick(Qt.Key_Space, Qt.MetaModifier|Qt.ShiftModifier);
2333 tryCompare(app.session.lastSurface, "keymapLayout", "fr");
2334 tryCompare(app.session.lastSurface, "keymapVariant", "");
2335
2336 // switch to app2, should also get "fr"
2337 ApplicationManager.requestFocusApplication("calendar-app");
2338 tryCompare(app2.session.lastSurface, "keymapLayout", "fr");
2339 tryCompare(app2.session.lastSurface, "keymapVariant", "");
2340 }
2277 }2341 }
2278}2342}

Subscribers

People subscribed via source and target branches