Merge lp:~mterry/unity8/greeter-focus into lp:unity8

Proposed by Michael Terry
Status: Superseded
Proposed branch: lp:~mterry/unity8/greeter-focus
Merge into: lp:unity8
Prerequisite: lp:~josharenson/unity8/slim_greeter_real_lightdm
Diff against target: 2260 lines (+1309/-134)
43 files modified
plugins/LightDM/CMakeLists.txt (+1/-2)
plugins/LightDM/FullLightDM/CMakeLists.txt (+4/-1)
plugins/LightDM/Greeter.cpp (+7/-2)
plugins/LightDM/Greeter.h (+3/-2)
plugins/LightDM/IntegratedLightDM/CMakeLists.txt (+2/-3)
plugins/LightDM/IntegratedLightDM/QLightDM/Greeter (+17/-0)
plugins/LightDM/IntegratedLightDM/QLightDM/SessionsModel (+17/-0)
plugins/LightDM/IntegratedLightDM/QLightDM/UsersModel (+17/-0)
plugins/LightDM/IntegratedLightDM/liblightdm/CMakeLists.txt (+2/-0)
plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModel.cpp (+88/-0)
plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModel.h (+72/-0)
plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.cpp (+36/-0)
plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.h (+54/-0)
plugins/LightDM/SessionsModel.cpp (+122/-0)
plugins/LightDM/SessionsModel.h (+68/-0)
plugins/LightDM/plugin.cpp (+17/-7)
qml/Greeter/Greeter.qml (+28/-24)
qml/Greeter/GreeterPrompt.qml (+64/-12)
qml/Greeter/LoginList.qml (+27/-16)
qml/Greeter/NarrowView.qml (+2/-1)
qml/Greeter/WideView.qml (+4/-3)
qml/Shell.qml (+5/-3)
tests/mocks/LightDM/IntegratedLightDM/CMakeLists.txt (+2/-0)
tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.cpp (+37/-0)
tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.h (+34/-0)
tests/mocks/LightDM/IntegratedLightDM/QLightDM/SessionsModel (+17/-0)
tests/mocks/LightDM/IntegratedLightDM/liblightdm/CMakeLists.txt (+2/-0)
tests/mocks/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp (+1/-1)
tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModel.cpp (+99/-0)
tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModel.h (+72/-0)
tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.cpp (+73/-0)
tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.h (+59/-0)
tests/mocks/LightDM/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp (+22/-22)
tests/mocks/LightDM/IntegratedLightDM/plugin.cpp (+11/-0)
tests/plugins/LightDM/IntegratedLightDM/CMakeLists.txt (+23/-0)
tests/plugins/LightDM/IntegratedLightDM/sessionsmodel.cpp (+96/-0)
tests/qmltests/Greeter/TestView.qml (+0/-1)
tests/qmltests/Greeter/tst_Greeter.qml (+2/-12)
tests/qmltests/Greeter/tst_WideView.qml (+90/-12)
tests/qmltests/Tutorial/tst_Tutorial.qml (+1/-1)
tests/qmltests/tst_OrientedShell.qml (+2/-2)
tests/qmltests/tst_Shell.qml (+3/-3)
tests/qmltests/tst_ShellWithPin.qml (+6/-4)
To merge this branch: bzr merge lp:~mterry/unity8/greeter-focus
Reviewer Review Type Date Requested Status
Unity8 CI Bot continuous-integration Needs Fixing
Unity Team Pending
Review via email: mp+298113@code.launchpad.net

This proposal has been superseded by a proposal from 2016-06-30.

Commit message

Fix tablet greeter focus to be always-on, no longer hiding the OSK or forcing a mouse click to type a password.

I've also squeezed some other small fixes in:

- Support Up and Down keys in user list.
- Don't accept non-digit characters when prompting for passcode (we ask OSK to show only digits, but due to bug 1586435, it shows other characters too; plus if the user has an external keyboard, they can type anything anyway).
- When prompting for a passcode in wide-view (tablet/desktop), stop at 4 digits just like we do in narrow-view.
- Don't show "Retry" during brief period before a prompt comes in from PAM.
- Don't let user drag user list if there's only one entry.

Now as for the focus changes...

One of the big reasons we lost keyboard focus before was because we set the shell to disabled whenever the greeter was "between PAM events" -- mostly so that the user can't swipe away greeter before we get our first prompt or are otherwise unsure about exactly what authentication is needed.

But disabled qml items can't be focused. So I've rejiggered things a bit. Instead of disabling the whole shell, I just disable the launcher, the indicators, and make the greeter non-swipeable.

I also do some tricks with the prompt so that it looks disabled (while we check with PAM about the password) but isn't actually. I don't want to make it look to the user that pressing backspace while they wait for PAM does anything, so after the user presses Enter, I present a fake label on top of the prompt that looks like a disabled prompt, while simultaneously hiding the prompt contents.

Description of the change

You could mostly just test this on your desktop. Changes are all to the wide-view side of things.

* Are there any related MPs required for this MP to build/function as expected? Please list.
 No

 * Did you perform an exploratory manual test run of your code change and any related functionality?
 Yes

 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
 NA

 * If you changed the UI, has there been a design review?
 NA

To post a comment you must log in.
Revision history for this message
Michael Terry (mterry) wrote :

I'm going to work on some qmltests now, but this branch can be reviewed and tested in meantime.

Revision history for this message
Michael Terry (mterry) wrote :

OK, tests added.

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

FAILED: Continuous integration, rev:2496
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/1588/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/2111
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=qmluitests.sh/1107
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/1107
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=yakkety,testname=qmluitests.sh/1107
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/2139
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/2050
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/2050
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=yakkety/2050
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/2041
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/2041/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/2041
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/2041/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=yakkety/2041
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=yakkety/2041/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/2041
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/2041/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/2041
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/2041/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=yakkety/2041
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=yakkety/2041/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/2041
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/2041/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/2041
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/2041/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=yakkety/2041
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=yakkety/2041/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

FAILED: Continuous integration, rev:2497
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/1591/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/2116
    FAILURE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=qmluitests.sh/1110/console
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/1110
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=yakkety,testname=qmluitests.sh/1110
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/2144
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/2054
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/2054
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=yakkety/2054
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/2045
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/2045/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/2045
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/2045/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=yakkety/2045
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=yakkety/2045/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/2045
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/2045/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/2045
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/2045/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=yakkety/2045
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=yakkety/2045/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/2045
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/2045/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/2045
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/2045/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=yakkety/2045
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=yakkety/2045/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

FAILED: Continuous integration, rev:2498
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/1594/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/2121
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=qmluitests.sh/1115
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/1115
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=yakkety,testname=qmluitests.sh/1115
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/2149
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/2059
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/2059
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=yakkety/2059
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/2050
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/2050/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/2050
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/2050/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=yakkety/2050
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=yakkety/2050/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/2050
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/2050/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/2050
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/2050/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=yakkety/2050
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=yakkety/2050/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/2050
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/2050/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/2050
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/2050/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=yakkety/2050
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=yakkety/2050/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
lp:~mterry/unity8/greeter-focus updated
1994. By Michael Terry

Fix focus on greeter and various other bugs

1995. By Michael Terry

Merge from slim_greeter_real_lightdm

1996. By Michael Terry

Whoops, reset po files that got changed somehow

1997. By Michael Terry

Fix minor issue when auto-responding with a passcode

1998. By Michael Terry

Merge parent

1999. By Michael Terry

Merge sessions-model

2000. By Michael Terry

Be smoother about focus in GreeterPrompt

2001. By Michael Terry

remove unneeded check, fixes test

2002. By Michael Terry

Merge in greeter-hide-indicators

2003. By Michael Terry

Update copyrights

2004. By Michael Terry

Give wideview test focus to start

2005. By Michael Terry

Merge focus refactor from dandrader

2006. By Michael Terry

Merge tryWideView focus refactor from dandrader

2007. By Michael Terry

Make it clear in GreeterPrompt that fakeLabel is for OSK focus

2008. By Michael Terry

Fix a test

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'plugins/LightDM/CMakeLists.txt'
2--- plugins/LightDM/CMakeLists.txt 2016-06-30 19:05:01 +0000
3+++ plugins/LightDM/CMakeLists.txt 2016-06-30 19:05:07 +0000
4@@ -7,9 +7,7 @@
5 ${CMAKE_CURRENT_SOURCE_DIR}
6 ${CMAKE_CURRENT_BINARY_DIR}
7 ${CMAKE_SOURCE_DIR}/plugins/Utils
8- ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM/IntegratedLightDM
9 ${CMAKE_BINARY_DIR}/tests/mocks/LightDM/IntegratedLightDM
10- ${LIBLIGHTDM_INCLUDE_DIRS}
11 ${libunity8-private_SOURCE_DIR}
12 ${LIBUSERMETRICSOUTPUT_INCLUDE_DIRS}
13 )
14@@ -20,6 +18,7 @@
15 DBusGreeterList.cpp
16 Greeter.cpp
17 plugin.cpp
18+ SessionsModel.cpp
19 UsersModel.cpp
20 )
21
22
23=== modified file 'plugins/LightDM/FullLightDM/CMakeLists.txt'
24--- plugins/LightDM/FullLightDM/CMakeLists.txt 2016-06-30 19:05:01 +0000
25+++ plugins/LightDM/FullLightDM/CMakeLists.txt 2016-06-30 19:05:07 +0000
26@@ -4,7 +4,10 @@
27
28 add_definitions(-DFULL_LIGHTDM)
29
30-include_directories(../)
31+include_directories(
32+ ../
33+ ${LIBLIGHTDM_INCLUDE_DIRS}
34+ )
35
36 foreach(source_file ${QMLPLUGIN_SRC})
37 list(APPEND RELATIVE_QMLPLUGIN_SRC ../${source_file})
38
39=== modified file 'plugins/LightDM/Greeter.cpp'
40--- plugins/LightDM/Greeter.cpp 2016-06-30 19:05:01 +0000
41+++ plugins/LightDM/Greeter.cpp 2016-06-30 19:05:07 +0000
42@@ -1,5 +1,5 @@
43 /*
44- * Copyright (C) 2013 Canonical, Ltd.
45+ * Copyright (C) 2013, 2015 Canonical, Ltd.
46 *
47 * This program is free software; you can redistribute it and/or modify
48 * it under the terms of the GNU General Public License as published by
49@@ -13,7 +13,6 @@
50 * You should have received a copy of the GNU General Public License
51 * along with this program. If not, see <http://www.gnu.org/licenses/>.
52 *
53- * Author: Michael Terry <michael.terry@canonical.com>
54 */
55
56 #include "Greeter.h"
57@@ -72,6 +71,12 @@
58 return d->m_greeter->authenticationUser();
59 }
60
61+QString Greeter::defaultSessionHint() const
62+{
63+ Q_D(const Greeter);
64+ return d->m_greeter->defaultSessionHint();
65+}
66+
67 bool Greeter::promptless() const
68 {
69 Q_D(const Greeter);
70
71=== modified file 'plugins/LightDM/Greeter.h'
72--- plugins/LightDM/Greeter.h 2016-06-30 19:05:01 +0000
73+++ plugins/LightDM/Greeter.h 2016-06-30 19:05:07 +0000
74@@ -1,5 +1,5 @@
75 /*
76- * Copyright (C) 2012,2013 Canonical, Ltd.
77+ * Copyright (C) 2012,2013,2015 Canonical, Ltd.
78 *
79 * This program is free software; you can redistribute it and/or modify
80 * it under the terms of the GNU General Public License as published by
81@@ -13,7 +13,6 @@
82 * You should have received a copy of the GNU General Public License
83 * along with this program. If not, see <http://www.gnu.org/licenses/>.
84 *
85- * Authors: Michael Terry <michael.terry@canonical.com>
86 */
87
88 /* This class is a really tiny filter around QLightDM::Greeter. There are some
89@@ -37,6 +36,7 @@
90 Q_PROPERTY(bool active READ isActive WRITE setIsActive NOTIFY isActiveChanged)
91 Q_PROPERTY(bool authenticated READ isAuthenticated NOTIFY isAuthenticatedChanged)
92 Q_PROPERTY(QString authenticationUser READ authenticationUser NOTIFY authenticationUserChanged)
93+ Q_PROPERTY(QString defaultSession READ defaultSessionHint CONSTANT)
94 Q_PROPERTY(bool promptless READ promptless NOTIFY promptlessChanged)
95 Q_PROPERTY(QString selectUser READ selectUser CONSTANT)
96
97@@ -46,6 +46,7 @@
98 bool isActive() const;
99 bool isAuthenticated() const;
100 QString authenticationUser() const;
101+ QString defaultSessionHint() const;
102 bool promptless() const;
103 QString selectUser() const;
104
105
106=== modified file 'plugins/LightDM/IntegratedLightDM/CMakeLists.txt'
107--- plugins/LightDM/IntegratedLightDM/CMakeLists.txt 2016-06-30 19:05:01 +0000
108+++ plugins/LightDM/IntegratedLightDM/CMakeLists.txt 2016-06-30 19:05:07 +0000
109@@ -7,9 +7,8 @@
110 endforeach()
111
112 include_directories(
113- ${QMLPLUGIN_INCLUDES}
114- ${libunity8-private_SOURCE_DIR}
115-)
116+ . #QLightDM
117+ )
118
119 foreach(source_file ${QMLPLUGIN_SRC})
120 list(APPEND RELATIVE_QMLPLUGIN_SRC ../${source_file})
121
122=== added directory 'plugins/LightDM/IntegratedLightDM/QLightDM'
123=== added file 'plugins/LightDM/IntegratedLightDM/QLightDM/Greeter'
124--- plugins/LightDM/IntegratedLightDM/QLightDM/Greeter 1970-01-01 00:00:00 +0000
125+++ plugins/LightDM/IntegratedLightDM/QLightDM/Greeter 2016-06-30 19:05:07 +0000
126@@ -0,0 +1,17 @@
127+/*
128+ * Copyright (C) 2016 Canonical, Ltd.
129+ *
130+ * This program is free software; you can redistribute it and/or modify
131+ * it under the terms of the GNU General Public License as published by
132+ * the Free Software Foundation; version 3.
133+ *
134+ * This program is distributed in the hope that it will be useful,
135+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
136+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
137+ * GNU General Public License for more details.
138+ *
139+ * You should have received a copy of the GNU General Public License
140+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
141+ */
142+
143+#include "../liblightdm/Greeter.h"
144
145=== added file 'plugins/LightDM/IntegratedLightDM/QLightDM/SessionsModel'
146--- plugins/LightDM/IntegratedLightDM/QLightDM/SessionsModel 1970-01-01 00:00:00 +0000
147+++ plugins/LightDM/IntegratedLightDM/QLightDM/SessionsModel 2016-06-30 19:05:07 +0000
148@@ -0,0 +1,17 @@
149+/*
150+ * Copyright (C) 2016 Canonical, Ltd.
151+ *
152+ * This program is free software; you can redistribute it and/or modify
153+ * it under the terms of the GNU General Public License as published by
154+ * the Free Software Foundation; version 3.
155+ *
156+ * This program is distributed in the hope that it will be useful,
157+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
158+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
159+ * GNU General Public License for more details.
160+ *
161+ * You should have received a copy of the GNU General Public License
162+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
163+ */
164+
165+#include "../liblightdm/SessionsModel.h"
166
167=== added file 'plugins/LightDM/IntegratedLightDM/QLightDM/UsersModel'
168--- plugins/LightDM/IntegratedLightDM/QLightDM/UsersModel 1970-01-01 00:00:00 +0000
169+++ plugins/LightDM/IntegratedLightDM/QLightDM/UsersModel 2016-06-30 19:05:07 +0000
170@@ -0,0 +1,17 @@
171+/*
172+ * Copyright (C) 2016 Canonical, Ltd.
173+ *
174+ * This program is free software; you can redistribute it and/or modify
175+ * it under the terms of the GNU General Public License as published by
176+ * the Free Software Foundation; version 3.
177+ *
178+ * This program is distributed in the hope that it will be useful,
179+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
180+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
181+ * GNU General Public License for more details.
182+ *
183+ * You should have received a copy of the GNU General Public License
184+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
185+ */
186+
187+#include "../liblightdm/UsersModel.h"
188
189=== modified file 'plugins/LightDM/IntegratedLightDM/liblightdm/CMakeLists.txt'
190--- plugins/LightDM/IntegratedLightDM/liblightdm/CMakeLists.txt 2016-06-30 19:05:01 +0000
191+++ plugins/LightDM/IntegratedLightDM/liblightdm/CMakeLists.txt 2016-06-30 19:05:07 +0000
192@@ -2,8 +2,10 @@
193
194 set(LibLightDM_SOURCES
195 Greeter.cpp
196+ SessionsModel.cpp
197 UsersModel.cpp
198 GreeterPrivate.cpp
199+ SessionsModelPrivate.cpp
200 UsersModelPrivate.cpp
201 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
202 ${CMAKE_SOURCE_DIR}/plugins/Utils/qvariantlistmodel.cpp
203
204=== added file 'plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModel.cpp'
205--- plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModel.cpp 1970-01-01 00:00:00 +0000
206+++ plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModel.cpp 2016-06-30 19:05:07 +0000
207@@ -0,0 +1,88 @@
208+/*
209+ * Copyright (C) 2015 Canonical, Ltd.
210+ *
211+ * This program is free software; you can redistribute it and/or modify
212+ * it under the terms of the GNU General Public License as published by
213+ * the Free Software Foundation; version 3.
214+ *
215+ * This program is distributed in the hope that it will be useful,
216+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
217+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
218+ * GNU General Public License for more details.
219+ *
220+ * You should have received a copy of the GNU General Public License
221+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
222+ *
223+ */
224+
225+
226+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
227+ * CHANGES MADE HERE MUST BE REFLECTED ON THE MOCK LIB
228+ * COUNTERPART IN tests/mocks/LightDM/IntegratedLightDM/liblightdm
229+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
230+
231+// LightDM currently is Qt4 compatible, and so doesn't define setRoleNames.
232+// To use the same method of setting role name that it does, we
233+// set our compatibility to Qt4 here too.
234+#define QT_DISABLE_DEPRECATED_BEFORE QT_VERSION_CHECK(4, 0, 0)
235+
236+#include "SessionsModel.h"
237+#include "SessionsModelPrivate.h"
238+#include <QtCore/QDir>
239+#include <QtCore/QString>
240+
241+namespace QLightDM
242+{
243+
244+SessionsModel::SessionsModel(QObject* parent) :
245+ QAbstractListModel(parent),
246+ d_ptr(new SessionsModelPrivate(this))
247+{
248+ Q_D(SessionsModel);
249+ m_roleNames = QAbstractListModel::roleNames();
250+ m_roleNames[KeyRole] = "key";
251+ m_roleNames[TypeRole] = "type";
252+}
253+
254+SessionsModel::~SessionsModel()
255+{
256+ delete d_ptr;
257+}
258+
259+QVariant SessionsModel::data(const QModelIndex& index, int role) const
260+{
261+ Q_D(const SessionsModel);
262+
263+ if(!index.isValid()) {
264+ return QVariant();
265+ }
266+
267+ int row = index.row();
268+
269+ switch (role) {
270+ case QLightDM::SessionsModel::KeyRole:
271+ return d->sessionItems[row].key;
272+ case Qt::DisplayRole:
273+ return d->sessionItems[row].name;
274+ default:
275+ return QVariant();
276+ }
277+}
278+
279+QHash<int, QByteArray> SessionsModel::roleNames() const
280+{
281+ return m_roleNames;
282+}
283+
284+int SessionsModel::rowCount(const QModelIndex& parent) const
285+{
286+ Q_D(const SessionsModel);
287+
288+ if (parent.isValid()) {
289+ return 0;
290+ } else { // parent is root
291+ return d->sessionItems.size();
292+ }
293+}
294+
295+} // namespace QLightDM
296
297=== added file 'plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModel.h'
298--- plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModel.h 1970-01-01 00:00:00 +0000
299+++ plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModel.h 2016-06-30 19:05:07 +0000
300@@ -0,0 +1,72 @@
301+/*
302+ * Copyright (C) 2015 Canonical, Ltd.
303+ *
304+ * This program is free software; you can redistribute it and/or modify
305+ * it under the terms of the GNU General Public License as published by
306+ * the Free Software Foundation; version 3.
307+ *
308+ * This program is distributed in the hope that it will be useful,
309+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
310+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
311+ * GNU General Public License for more details.
312+ *
313+ * You should have received a copy of the GNU General Public License
314+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
315+ *
316+ */
317+
318+#ifndef UNITY_INTEGRATED_SESSIONSMODEL_H
319+#define UNITY_INTEGRATED_SESSIONSMODEL_H
320+
321+#include <QtCore/QAbstractListModel>
322+#include <QtCore/QString>
323+
324+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
325+ * CHANGES MADE HERE MUST BE REFLECTED ON THE MOCK LIB
326+ * COUNTERPART IN tests/mocks/LightDM/IntegratedLightDM/liblightdm
327+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
328+
329+// This is taken from liblightdm and modified to confirm to our syle conventions
330+namespace QLightDM
331+{
332+class SessionsModelPrivate;
333+
334+class Q_DECL_EXPORT SessionsModel : public QAbstractListModel
335+ {
336+ Q_OBJECT
337+
338+ Q_ENUMS(SessionModelRoles SessionType)
339+
340+ public:
341+
342+ enum SessionModelRoles {
343+ //name is exposed as Qt::DisplayRole
344+ //comment is exposed as Qt::TooltipRole
345+ KeyRole = Qt::UserRole,
346+ IdRole = KeyRole, /** Deprecated */
347+ TypeRole
348+ };
349+
350+ enum SessionType {
351+ LocalSessions,
352+ RemoteSessions
353+ };
354+
355+ explicit SessionsModel(QObject* parent=0); /** Deprecated. Loads local sessions*/
356+ explicit SessionsModel(SessionsModel::SessionType, QObject* parent=0);
357+ virtual ~SessionsModel();
358+
359+ QHash<int, QByteArray> roleNames() const override;
360+ int rowCount(const QModelIndex& parent) const override;
361+ QVariant data(const QModelIndex& index, int role) const override;
362+
363+ protected:
364+ SessionsModelPrivate* const d_ptr;
365+
366+ private:
367+ QHash<int, QByteArray> m_roleNames;
368+ Q_DECLARE_PRIVATE(SessionsModel)
369+ };
370+}
371+
372+#endif // UNITY_INTEGRATED_SESSIONSMODEL_H
373
374=== added file 'plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.cpp'
375--- plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.cpp 1970-01-01 00:00:00 +0000
376+++ plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.cpp 2016-06-30 19:05:07 +0000
377@@ -0,0 +1,36 @@
378+/*
379+ * Copyright (C) 2015 Canonical, Ltd.
380+ *
381+ * This program is free software; you can redistribute it and/or modify
382+ * it under the terms of the GNU General Public License as published by
383+ * the Free Software Foundation; version 3.
384+ *
385+ * This program is distributed in the hope that it will be useful,
386+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
387+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
388+ * GNU General Public License for more details.
389+ *
390+ * You should have received a copy of the GNU General Public License
391+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
392+ *
393+ */
394+
395+#include "SessionsModelPrivate.h"
396+
397+#include <QDir>
398+#include <QSettings>
399+#include <QStringList>
400+
401+namespace QLightDM
402+{
403+
404+SessionsModelPrivate::SessionsModelPrivate(SessionsModel* parent)
405+ : q_ptr(parent)
406+{
407+ // Since this model is never visible, as there is only 1 session,
408+ // just use the session name as the key
409+ QString sessionName = qgetenv("XDG_SESSION_DESKTOP");
410+ sessionItems.append({sessionName, "", sessionName, ""});
411+}
412+
413+} // namespace QLightDM
414
415=== added file 'plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.h'
416--- plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.h 1970-01-01 00:00:00 +0000
417+++ plugins/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.h 2016-06-30 19:05:07 +0000
418@@ -0,0 +1,54 @@
419+/*
420+ * Copyright (C) 2015 Canonical, Ltd.
421+ *
422+ * This program is free software; you can redistribute it and/or modify
423+ * it under the terms of the GNU General Public License as published by
424+ * the Free Software Foundation; version 3.
425+ *
426+ * This program is distributed in the hope that it will be useful,
427+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
428+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
429+ * GNU General Public License for more details.
430+ *
431+ * You should have received a copy of the GNU General Public License
432+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
433+ *
434+ */
435+
436+#ifndef UNITY_INTEGRATED_SESSIONSMODEL_PRIVATE_H
437+#define UNITY_INTEGRATED_SESSIONSMODEL_PRIVATE_H
438+
439+#include <QtCore/QList>
440+#include <QtCore/QString>
441+
442+namespace QLightDM
443+{
444+class SessionsModel;
445+
446+class SessionItem
447+{
448+public:
449+ QString key;
450+ QString type; // unused
451+ QString name;
452+ QString comment; // unused
453+};
454+
455+class SessionsModelPrivate
456+{
457+public:
458+ explicit SessionsModelPrivate(SessionsModel* parent=0);
459+ virtual ~SessionsModelPrivate() = default;
460+
461+ QList<SessionItem> sessionItems;
462+
463+protected:
464+ SessionsModel* const q_ptr;
465+
466+private:
467+ Q_DECLARE_PUBLIC(SessionsModel)
468+};
469+
470+} // namespace QLightDM
471+
472+#endif // UNITY_INTEGRATED_SESSIONSMODEL_PRIVATE_H
473
474=== added file 'plugins/LightDM/SessionsModel.cpp'
475--- plugins/LightDM/SessionsModel.cpp 1970-01-01 00:00:00 +0000
476+++ plugins/LightDM/SessionsModel.cpp 2016-06-30 19:05:07 +0000
477@@ -0,0 +1,122 @@
478+/*
479+ * Copyright (C) 2015 Canonical, Ltd.
480+ *
481+ * This program is free software; you can redistribute it and/or modify
482+ * it under the terms of the GNU General Public License as published by
483+ * the Free Software Foundation; version 3.
484+ *
485+ * This program is distributed in the hope that it will be useful,
486+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
487+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
488+ * GNU General Public License for more details.
489+ *
490+ * You should have received a copy of the GNU General Public License
491+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
492+ *
493+ */
494+
495+#include "SessionsModel.h"
496+#include <QtCore/QFile>
497+#include <QtCore/QSortFilterProxyModel>
498+
499+QHash<int, QByteArray> SessionsModel::roleNames() const
500+{
501+ return m_roleNames;
502+}
503+
504+int SessionsModel::rowCount(const QModelIndex& parent) const
505+{
506+ return m_model->rowCount(parent);
507+}
508+
509+QList<QUrl> SessionsModel::iconSearchDirectories() const
510+{
511+ return m_iconSearchDirectories;
512+}
513+
514+void SessionsModel::setIconSearchDirectories(const QList<QUrl> searchDirectories)
515+{
516+ // QML gives us a url with file:// prepended which breaks QFile::exists()
517+ // so convert the url to a local file
518+ QList<QUrl> localList = {};
519+ Q_FOREACH(const QUrl& searchDirectory, searchDirectories)
520+ {
521+ localList.append(searchDirectory.toLocalFile());
522+ }
523+ m_iconSearchDirectories = localList;
524+ Q_EMIT iconSearchDirectoriesChanged();
525+}
526+
527+QUrl SessionsModel::iconUrl(const QString sessionName) const
528+{
529+ Q_FOREACH(const QUrl& searchDirectory, m_iconSearchDirectories)
530+ {
531+ // This is an established icon naming convention
532+ QString customIconUrl = searchDirectory.toString(QUrl::StripTrailingSlash) +
533+ "/custom_" + sessionName + "_badge.png";
534+ QString iconUrl = searchDirectory.toString(QUrl::StripTrailingSlash) +
535+ "/" + sessionName + "_badge.png";
536+
537+ QFile customIconFile(customIconUrl);
538+ QFile iconFile(iconUrl);
539+ if (customIconFile.exists()) {
540+ return QUrl(customIconUrl);
541+ } else if (iconFile.exists()) {
542+ return QUrl(iconUrl);
543+ } else{
544+ // Search the legacy way
545+ QString path = searchDirectory.toString(QUrl::StripTrailingSlash) + "/";
546+ if (sessionName == "ubuntu" || sessionName == "ubuntu-2d") {
547+ path += "ubuntu_badge.png";
548+ } else if(
549+ sessionName == "gnome-classic" ||
550+ sessionName == "gnome-flashback-compiz" ||
551+ sessionName == "gnome-flashback-metacity" ||
552+ sessionName == "gnome-shell" ||
553+ sessionName == "gnome-wayland" ||
554+ sessionName == "gnome"
555+ ){
556+ path += "gnome_badge.png";
557+ } else if (sessionName == "plasma") {
558+ path += "kde_badge.png";
559+ } else if (sessionName == "xterm") {
560+ path += "recovery_console_badge.png";
561+ } else if (sessionName == "remote-login") {
562+ path += "remote_login_help.png";
563+ }
564+
565+ if (QFile(path).exists()) {
566+ return path;
567+ }
568+ }
569+ }
570+
571+ // FIXME make this smarter
572+ return QUrl("./graphics/session_icons/unknown_badge.png");
573+}
574+
575+QVariant SessionsModel::data(const QModelIndex& index, int role) const
576+{
577+ switch (role) {
578+ case SessionsModel::IconRole:
579+ return iconUrl(m_model->data(index, Qt::DisplayRole).toString());
580+ default:
581+ return m_model->data(index, role);
582+ }
583+}
584+
585+SessionsModel::SessionsModel(QObject* parent)
586+ : UnitySortFilterProxyModelQML(parent)
587+{
588+ // Add a custom IconRole that isn't in either of the lightdm implementations
589+ m_model = new QLightDM::SessionsModel(this);
590+ m_roleNames = m_model->roleNames();
591+ m_roleNames[IconRole] = "icon_url";
592+
593+ setModel(m_model);
594+ setSourceModel(m_model);
595+ setSortCaseSensitivity(Qt::CaseInsensitive);
596+ setSortLocaleAware(true);
597+ setSortRole(Qt::DisplayRole);
598+ sort(0);
599+}
600
601=== added file 'plugins/LightDM/SessionsModel.h'
602--- plugins/LightDM/SessionsModel.h 1970-01-01 00:00:00 +0000
603+++ plugins/LightDM/SessionsModel.h 2016-06-30 19:05:07 +0000
604@@ -0,0 +1,68 @@
605+/*
606+ * Copyright (C) 2015 Canonical, Ltd.
607+ *
608+ * This program is free software; you can redistribute it and/or modify
609+ * it under the terms of the GNU General Public License as published by
610+ * the Free Software Foundation; version 3.
611+ *
612+ * This program is distributed in the hope that it will be useful,
613+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
614+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
615+ * GNU General Public License for more details.
616+ *
617+ * You should have received a copy of the GNU General Public License
618+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
619+ *
620+ */
621+
622+#ifndef UNITY_SESSIONSMODEL_H
623+#define UNITY_SESSIONSMODEL_H
624+
625+#include <unitysortfilterproxymodelqml.h>
626+#include <QLightDM/SessionsModel>
627+#include <QtCore/QHash>
628+#include <QtCore/QObject>
629+#include <QtCore/QUrl>
630+
631+class SessionsModel : public UnitySortFilterProxyModelQML
632+{
633+ Q_OBJECT
634+
635+ Q_ENUMS(SessionModelRoles)
636+
637+ Q_PROPERTY(QList<QUrl> iconSearchDirectories READ iconSearchDirectories
638+ WRITE setIconSearchDirectories NOTIFY iconSearchDirectoriesChanged)
639+Q_SIGNALS:
640+ void iconSearchDirectoriesChanged();
641+
642+public:
643+ enum SessionModelRoles {
644+ /* This is tricky / ugly. Since we are ultimately chaining 3 enums together,
645+ * the _first_ value of this enum MUST be the _last_ value of
646+ * QLightDM::SessionsModel::SessionModelRoles and consquently, this must
647+ * also match the last value in the corresponding enum of the integrated lib
648+ */
649+ TypeRole = QLightDM::SessionsModel::SessionModelRoles::TypeRole,
650+ IconRole
651+ };
652+
653+ explicit SessionsModel(QObject* parent=nullptr);
654+
655+ QHash<int, QByteArray> roleNames() const override;
656+ int rowCount(const QModelIndex& parent) const override;
657+ QVariant data(const QModelIndex& index, int role) const override;
658+ QList<QUrl> iconSearchDirectories() const;
659+ Q_INVOKABLE QUrl iconUrl(const QString sessionName) const;
660+
661+ void setIconSearchDirectories(const QList<QUrl> searchDirectories);
662+private:
663+ QLightDM::SessionsModel* m_model;
664+ QHash<int, QByteArray> m_roleNames;
665+ QList<QUrl> m_iconSearchDirectories{
666+ QUrl("/usr/share/unity8/Greeter/graphics/session_icons"),
667+ QUrl("/usr/local/share/unity-greeter"),
668+ QUrl("/usr/share/unity-greeter/")};
669+
670+};
671+
672+#endif // UNITY_SESSIONSMODEL_H
673
674=== modified file 'plugins/LightDM/plugin.cpp'
675--- plugins/LightDM/plugin.cpp 2016-06-30 19:05:01 +0000
676+++ plugins/LightDM/plugin.cpp 2016-06-30 19:05:07 +0000
677@@ -19,9 +19,11 @@
678 #include "DBusGreeter.h"
679 #include "DBusGreeterList.h"
680 #include "Greeter.h"
681+#include "SessionsModel.h"
682 #include "UsersModel.h"
683 #include <libusermetricsoutput/ColorTheme.h>
684 #include <libusermetricsoutput/UserMetrics.h>
685+#include <QLightDM/SessionsModel>
686 #include <QLightDM/UsersModel>
687
688 #include <QAbstractItemModel>
689@@ -40,6 +42,13 @@
690 return greeter;
691 }
692
693+static QObject *sessions_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
694+{
695+ Q_UNUSED(engine)
696+ Q_UNUSED(scriptEngine)
697+ return new SessionsModel();
698+}
699+
700 static QObject *users_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
701 {
702 Q_UNUSED(engine)
703@@ -61,21 +70,22 @@
704
705 #if defined INTEGRATED_LIGHTDM
706 Q_ASSERT(uri == QLatin1String("LightDM.IntegratedLightDM"));
707+ qmlRegisterSingletonType<Greeter>(uri, 0, 1, "Greeter", greeter_provider);
708 #elif defined FULL_LIGHTDM
709 Q_ASSERT(uri == QLatin1String("LightDM.FullLightDM"));
710-#endif
711- qRegisterMetaType<QLightDM::Greeter::MessageType>("QLightDM::Greeter::MessageType");
712- qRegisterMetaType<QLightDM::Greeter::PromptType>("QLightDM::Greeter::PromptType");
713-
714-#if defined INTEGRATED_LIGHTDM
715- qmlRegisterSingletonType<Greeter>(uri, 0, 1, "Greeter", greeter_provider);
716-#elif defined FULL_LIGHTDM
717 qmlRegisterSingletonType<QLightDM::Greeter>(uri, 0, 1, "Greeter", greeter_provider);
718 #else
719 #error No library defined in LightDM plugin
720 #endif
721
722+ qRegisterMetaType<QLightDM::Greeter::MessageType>("QLightDM::Greeter::MessageType");
723+ qRegisterMetaType<QLightDM::Greeter::PromptType>("QLightDM::Greeter::PromptType");
724+
725+ qmlRegisterSingletonType<SessionsModel>(uri, 0, 1, "Sessions", sessions_provider);
726+ qmlRegisterUncreatableType<SessionsModel>(uri, 0, 1, "SessionRoles", QStringLiteral("Type is not instantiable"));
727+
728 qmlRegisterSingletonType<UsersModel>(uri, 0, 1, "Users", users_provider);
729 qmlRegisterUncreatableType<QLightDM::UsersModel>(uri, 0, 1, "UserRoles", QStringLiteral("Type is not instantiable"));
730+
731 qmlRegisterSingletonType<UserMetricsOutput::UserMetrics>(uri, 0, 1, "Infographic", infographic_provider);
732 }
733
734=== modified file 'qml/Greeter/Greeter.qml'
735--- qml/Greeter/Greeter.qml 2016-06-30 19:05:01 +0000
736+++ qml/Greeter/Greeter.qml 2016-06-30 19:05:07 +0000
737@@ -163,35 +163,41 @@
738 return -1;
739 }
740
741- function selectUser(uid, reset) {
742- if (uid < 0)
743+ function selectUser(index, reset) {
744+ if (index < 0 || index >= LightDMService.users.count)
745 return;
746 d.waiting = true;
747 if (reset) {
748 loader.item.reset();
749 }
750- currentIndex = uid;
751- var user = LightDMService.users.data(uid, LightDMService.userRoles.NameRole);
752+ currentIndex = index;
753+ var user = LightDMService.users.data(index, LightDMService.userRoles.NameRole);
754 AccountsService.user = user;
755 LauncherModel.setUser(user);
756 LightDMService.greeter.authenticate(user); // always resets auth state
757 }
758
759+ function hideView() {
760+ if (loader.item) {
761+ loader.item.enabled = false; // drop OSK and prevent interaction
762+ loader.item.notifyAuthenticationSucceeded(false /* showFakePassword */);
763+ loader.item.hide();
764+ }
765+ }
766+
767 function login() {
768- enabled = false;
769+ d.waiting = true;
770 if (LightDMService.greeter.startSessionSync()) {
771 sessionStarted();
772- if (loader.item) {
773- loader.item.notifyAuthenticationSucceeded(false /* showFakePassword */);
774- }
775+ hideView();
776 } else if (loader.item) {
777 loader.item.notifyAuthenticationFailed();
778 }
779- enabled = true;
780+ d.waiting = false;
781 }
782
783 function startUnlock(toTheRight) {
784- if (loader.item) {
785+ if (loader.item && !d.waiting) {
786 return loader.item.tryToUnlock(toTheRight);
787 } else {
788 return false;
789@@ -199,10 +205,8 @@
790 }
791
792 function checkForcedUnlock(hideNow) {
793- if (forcedUnlock && shown && loader.item) {
794- // pretend we were just authenticated
795- loader.item.notifyAuthenticationSucceeded(false /* showFakePassword */);
796- loader.item.hide();
797+ if (forcedUnlock && shown) {
798+ hideView();
799 if (hideNow) {
800 root.hideNow(); // skip hide animation
801 }
802@@ -323,7 +327,7 @@
803
804 onLoaded: {
805 root.lockedApp = "";
806- root.forceActiveFocus();
807+ item.forceActiveFocus();
808 d.selectUser(d.currentIndex, true);
809 LightDMService.infographic.readyForDataChange();
810 }
811@@ -333,13 +337,11 @@
812 onSelected: {
813 d.selectUser(index, true);
814 }
815- onPromptlessLogin: d.login();
816 onResponded: {
817 if (root.locked) {
818 LightDMService.greeter.respond(response);
819 } else {
820 d.login();
821- loader.item.hide();
822 }
823 }
824 onTease: root.tease()
825@@ -389,6 +391,12 @@
826
827 Binding {
828 target: loader.item
829+ property: "waiting"
830+ value: d.waiting
831+ }
832+
833+ Binding {
834+ target: loader.item
835 property: "alphanumeric"
836 value: d.alphanumeric
837 }
838@@ -417,10 +425,7 @@
839
840 onShowGreeter: root.forceShow()
841
842- onHideGreeter: {
843- d.login();
844- loader.item.hide();
845- }
846+ onHideGreeter: d.login()
847
848 onShowMessage: {
849 // inefficient, but we only rarely deal with messages
850@@ -438,11 +443,11 @@
851 }
852
853 onShowPrompt: {
854- d.waiting = false;
855-
856 if (loader.item) {
857 loader.item.showPrompt(text, isSecret, isDefaultPrompt);
858 }
859+
860+ d.waiting = false;
861 }
862
863 onAuthenticationComplete: {
864@@ -451,7 +456,6 @@
865 if (LightDMService.greeter.authenticated) {
866 if (!LightDMService.greeter.promptless) {
867 d.login();
868- loader.item.hide();
869 }
870 } else {
871 if (!LightDMService.greeter.promptless) {
872
873=== modified file 'qml/Greeter/GreeterPrompt.qml'
874--- qml/Greeter/GreeterPrompt.qml 2016-06-06 18:21:22 +0000
875+++ qml/Greeter/GreeterPrompt.qml 2016-06-30 19:05:07 +0000
876@@ -16,6 +16,7 @@
877
878 import QtQuick 2.4
879 import Ubuntu.Components 1.3
880+import "../Components"
881
882 FocusScope {
883 id: root
884@@ -33,6 +34,7 @@
885
886 function reset() {
887 passwordInput.text = "";
888+ fakeLabel.text = "";
889 d.enabled = true;
890 }
891
892@@ -62,10 +64,14 @@
893 objectName: "promptButton"
894 anchors.fill: parent
895 visible: !root.isPrompt
896- enabled: d.enabled
897 focus: visible
898
899- onClicked: root.clicked()
900+ onClicked: {
901+ if (d.enabled) {
902+ d.enabled = false;
903+ root.clicked();
904+ }
905+ }
906
907 Label {
908 anchors.centerIn: parent
909@@ -79,10 +85,22 @@
910 objectName: "promptField"
911 anchors.fill: parent
912 visible: root.isPrompt
913- enabled: d.enabled
914- focus: visible
915-
916- inputMethodHints: root.isAlphanumeric ? Qt.ImhNone : Qt.ImhDigitsOnly
917+ opacity: fakeLabel.visible ? 0 : 1
918+
919+ // Oddly, a simple "focus: visible" does not stick -- the binding
920+ // gets lost when switching between buttons and prompts. But this
921+ // explicit binding does work.
922+ Binding on focus {
923+ value: passwordInput.visible
924+ }
925+
926+ validator: RegExpValidator {
927+ regExp: root.isAlphanumeric ? /^.*$/ : /^\d{4}$/
928+ }
929+
930+ inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText |
931+ Qt.ImhMultiLine | // so OSK doesn't close on Enter
932+ (root.isAlphanumeric ? Qt.ImhNone : Qt.ImhDigitsOnly)
933 echoMode: root.isSecret ? TextInput.Password : TextInput.Normal
934 hasClearButton: false
935
936@@ -107,17 +125,35 @@
937 width: units.gu(3)
938 color: d.textColor
939 visible: root.isSecret && false // TODO: detect when caps lock is on
940+ readonly property real visibleWidth: visible ? width + passwordInput.frameSpacing : 0
941 }
942 ]
943
944+ onDisplayTextChanged: {
945+ // We use onDisplayTextChanged instead of onTextChanged because
946+ // displayText changes after text and if we did this before it
947+ // updated, we would use the wrong displayText for fakeLabel.
948+ if (!isAlphanumeric && text.length >= 4) {
949+ // hard limit of 4 for passcodes right now
950+ respond();
951+ }
952+ }
953+
954 onAccepted: {
955- if (!enabled)
956- return;
957+ if (d.enabled)
958+ respond();
959+ }
960+
961+ function respond() {
962 d.enabled = false;
963+ fakeLabel.text = displayText;
964 root.responded(text);
965 }
966
967- Keys.onEscapePressed: root.canceled()
968+ Keys.onEscapePressed: {
969+ root.canceled();
970+ event.accepted = true;
971+ }
972
973 // We use our own custom placeholder label instead of the standard
974 // TextField one because the standard one hardcodes baseText as the
975@@ -129,9 +165,7 @@
976 right: parent.right
977 verticalCenter: parent.verticalCenter
978 leftMargin: units.gu(1.5)
979- rightMargin: anchors.leftMargin +
980- (capsIcon.visible ? capsIcon.width + passwordInput.frameSpacing
981- : 0)
982+ rightMargin: anchors.leftMargin + capsIcon.visibleWidth
983 }
984 text: root.text
985 visible: passwordInput.text == "" && !passwordInput.inputMethodComposing
986@@ -139,4 +173,22 @@
987 elide: Text.ElideRight
988 }
989 }
990+
991+ // Have a fake label that covers the text field after the user presses
992+ // enter. What we *really* want is a disabled mode that doesn't lose
993+ // focus. Because our goal here is simply to keep the OSK up while
994+ // we wait for PAM to get back to us, and while waiting, we don't want
995+ // the user to be able to edit the field (simply because it would look
996+ // weird if we allowed that). But until we have such a disabled mode,
997+ // we'll fake it by covering the real text field with a label.
998+ FadingLabel {
999+ id: fakeLabel
1000+ anchors.verticalCenter: parent.verticalCenter
1001+ anchors.left: parent.left
1002+ anchors.right: parent.right
1003+ anchors.leftMargin: passwordInput.frameSpacing * 2
1004+ anchors.rightMargin: passwordInput.frameSpacing * 2 + capsIcon.visibleWidth
1005+ color: d.drawColor
1006+ visible: root.isPrompt && !d.enabled
1007+ }
1008 }
1009
1010=== modified file 'qml/Greeter/LoginList.qml'
1011--- qml/Greeter/LoginList.qml 2016-06-30 19:05:01 +0000
1012+++ qml/Greeter/LoginList.qml 2016-06-30 19:05:07 +0000
1013@@ -21,11 +21,13 @@
1014
1015 StyledItem {
1016 id: root
1017+ focus: true
1018
1019 property alias model: userList.model
1020 property bool alphanumeric: true
1021 property int currentIndex
1022 property bool locked
1023+ property bool waiting
1024
1025 readonly property int numAboveBelow: 4
1026 readonly property int cellHeight: units.gu(5)
1027@@ -36,7 +38,6 @@
1028
1029 signal selected(int index)
1030 signal responded(string response)
1031- signal promptlessLogin()
1032
1033 function tryToUnlock() {
1034 if (wasPrompted) {
1035@@ -45,7 +46,6 @@
1036 if (root.locked) {
1037 root.selected(currentIndex);
1038 } else {
1039- promptlessLogin();
1040 root.responded("");
1041 }
1042 }
1043@@ -60,20 +60,18 @@
1044 }
1045
1046 function showPrompt(text, isSecret, isDefaultPrompt) {
1047- d.promptText = text;
1048- passwordInput.reset();
1049+ passwordInput.text = isDefaultPrompt ? alphanumeric ? i18n.tr("Passphrase")
1050+ : i18n.tr("Passcode")
1051+ : text;
1052+ passwordInput.isPrompt = true;
1053 passwordInput.isSecret = isSecret;
1054- if (wasPrompted) // stay in text field if second prompt
1055- passwordInput.focus = true;
1056+ passwordInput.reset();
1057 wasPrompted = true;
1058 }
1059
1060 function showError() {
1061 wrongPasswordAnimation.start();
1062 root.resetAuthentication();
1063- if (wasPrompted) {
1064- passwordInput.focus = true;
1065- }
1066 }
1067
1068 function reset() {
1069@@ -83,15 +81,33 @@
1070 QtObject {
1071 id: d
1072
1073- property string promptText
1074+ function checkIfPromptless() {
1075+ if (!waiting && !wasPrompted) {
1076+ passwordInput.isPrompt = false;
1077+ passwordInput.text = root.locked ? i18n.tr("Retry")
1078+ : i18n.tr("Log In")
1079+ }
1080+ }
1081 }
1082
1083+ onWaitingChanged: d.checkIfPromptless()
1084+ onLockedChanged: d.checkIfPromptless()
1085+
1086 theme: ThemeSettings {
1087 name: "Ubuntu.Components.Themes.Ambiance"
1088 }
1089
1090+ Keys.onUpPressed: {
1091+ selected(currentIndex - 1);
1092+ event.accepted = true;
1093+ }
1094+ Keys.onDownPressed: {
1095+ selected(currentIndex + 1);
1096+ event.accepted = true;
1097+ }
1098 Keys.onEscapePressed: {
1099 selected(currentIndex);
1100+ event.accepted = true;
1101 }
1102
1103 onCurrentIndexChanged: {
1104@@ -137,6 +153,7 @@
1105 highlightRangeMode: ListView.StrictlyEnforceRange
1106 highlightMoveDuration: root.moveDuration
1107 flickDeceleration: 10000
1108+ interactive: count > 1
1109
1110 readonly property bool movingInternally: moveTimer.running || userList.moving
1111 onMovingInternallyChanged: {
1112@@ -254,13 +271,8 @@
1113 width: highlightItem.width - anchors.margins * 2
1114 opacity: userList.movingInternally ? 0 : 1
1115
1116- isPrompt: root.wasPrompted
1117 isAlphanumeric: root.alphanumeric
1118
1119- text: root.wasPrompted ? d.promptText
1120- : (root.locked ? i18n.tr("Retry")
1121- : i18n.tr("Log In"))
1122-
1123 onClicked: root.tryToUnlock()
1124 onResponded: root.responded(text)
1125 onCanceled: root.selected(currentIndex)
1126@@ -280,7 +292,6 @@
1127 return;
1128 }
1129 infoLabel.text = "";
1130- d.promptText = "";
1131 passwordInput.reset();
1132 root.wasPrompted = false;
1133 }
1134
1135=== modified file 'qml/Greeter/NarrowView.qml'
1136--- qml/Greeter/NarrowView.qml 2016-06-20 20:23:28 +0000
1137+++ qml/Greeter/NarrowView.qml 2016-06-30 19:05:07 +0000
1138@@ -31,6 +31,7 @@
1139 property bool alphanumeric
1140 property var userModel // unused
1141 property alias infographicModel: coverPage.infographicModel
1142+ property bool waiting
1143 readonly property bool fullyShown: coverPage.showProgress === 1 || lockscreen.shown
1144 readonly property bool required: coverPage.required || lockscreen.required
1145 readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
1146@@ -71,7 +72,6 @@
1147 if (!alphanumeric && showFakePassword) {
1148 lockscreen.showText("...."); // actual text doesn't matter, we show bullets
1149 }
1150- lockscreen.hide();
1151 }
1152
1153 function notifyAuthenticationFailed() {
1154@@ -172,6 +172,7 @@
1155 height: parent.height
1156 width: parent.width
1157 background: root.background
1158+ draggable: !root.waiting
1159 onTease: root.tease()
1160 onClicked: hide()
1161
1162
1163=== modified file 'qml/Greeter/WideView.qml'
1164--- qml/Greeter/WideView.qml 2016-06-30 19:05:01 +0000
1165+++ qml/Greeter/WideView.qml 2016-06-30 19:05:07 +0000
1166@@ -19,6 +19,7 @@
1167
1168 FocusScope {
1169 id: root
1170+ focus: true
1171
1172 property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin
1173 property alias launcherOffset: coverPage.launcherOffset
1174@@ -30,6 +31,7 @@
1175 property alias alphanumeric: loginList.alphanumeric
1176 property alias userModel: loginList.model
1177 property alias infographicModel: coverPage.infographicModel
1178+ property bool waiting
1179 readonly property bool fullyShown: coverPage.showProgress === 1
1180 readonly property bool required: coverPage.required
1181 readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
1182@@ -37,7 +39,6 @@
1183 // so that it can be replaced in tests with a mock object
1184 property var inputMethod: Qt.inputMethod
1185
1186- signal promptlessLogin()
1187 signal selected(int index)
1188 signal responded(string response)
1189 signal tease()
1190@@ -102,7 +103,7 @@
1191 objectName: "coverPage"
1192 height: parent.height
1193 width: parent.width
1194- draggable: !root.locked
1195+ draggable: !root.locked && !root.waiting
1196
1197 infographics {
1198 height: 0.75 * parent.height
1199@@ -132,8 +133,8 @@
1200 Behavior on height { UbuntuNumberAnimation {} }
1201
1202 locked: root.locked
1203+ waiting: root.waiting
1204
1205- onPromptlessLogin: root.promptlessLogin()
1206 onSelected: root.selected(index)
1207 onResponded: root.responded(response)
1208 }
1209
1210=== modified file 'qml/Shell.qml'
1211--- qml/Shell.qml 2016-06-30 19:05:01 +0000
1212+++ qml/Shell.qml 2016-06-30 19:05:07 +0000
1213@@ -137,9 +137,9 @@
1214 // For autopilot consumption
1215 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
1216
1217- // Disable everything while greeter is waiting, so that the user can't swipe
1218- // the greeter or launcher until we know whether the session is locked.
1219- enabled: greeter && !greeter.waiting
1220+ // Note when greeter is waiting on PAM, so that we can disable edges until
1221+ // we know which user data to show and whether the session is locked.
1222+ readonly property bool waitingOnGreeter: greeter && greeter.waiting
1223
1224 property real edgeSize: units.gu(settings.edgeDragWidth)
1225
1226@@ -528,6 +528,7 @@
1227 available: tutorial.panelEnabled
1228 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
1229 && (!greeter || !greeter.hasLockedApp)
1230+ && !shell.waitingOnGreeter
1231 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
1232
1233 minimizedPanelHeight: units.gu(3)
1234@@ -578,6 +579,7 @@
1235 available: tutorial.launcherEnabled
1236 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
1237 && !greeter.hasLockedApp
1238+ && !shell.waitingOnGreeter
1239 inverted: shell.usageScenario !== "desktop"
1240 superPressed: physicalKeysMapper.superPressed
1241 superTabPressed: physicalKeysMapper.superTabPressed
1242
1243=== modified file 'tests/mocks/LightDM/IntegratedLightDM/CMakeLists.txt'
1244--- tests/mocks/LightDM/IntegratedLightDM/CMakeLists.txt 2016-06-30 19:05:01 +0000
1245+++ tests/mocks/LightDM/IntegratedLightDM/CMakeLists.txt 2016-06-30 19:05:07 +0000
1246@@ -19,9 +19,11 @@
1247 ${CMAKE_SOURCE_DIR}/plugins/LightDM/DBusGreeter.cpp
1248 ${CMAKE_SOURCE_DIR}/plugins/LightDM/DBusGreeterList.cpp
1249 ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
1250+ ${CMAKE_SOURCE_DIR}/plugins/LightDM/SessionsModel.cpp
1251 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
1252 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
1253 MockGreeter.cpp
1254+ MockSessionsModel.cpp
1255 MockUsersModel.cpp
1256 plugin.cpp
1257 )
1258
1259=== added file 'tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.cpp'
1260--- tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.cpp 1970-01-01 00:00:00 +0000
1261+++ tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.cpp 2016-06-30 19:05:07 +0000
1262@@ -0,0 +1,37 @@
1263+/*
1264+ * Copyright (C) 2015 Canonical, Ltd.
1265+ *
1266+ * This program is free software; you can redistribute it and/or modify
1267+ * it under the terms of the GNU General Public License as published by
1268+ * the Free Software Foundation; version 3.
1269+ *
1270+ * This program is distributed in the hope that it will be useful,
1271+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1272+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1273+ * GNU General Public License for more details.
1274+ *
1275+ * You should have received a copy of the GNU General Public License
1276+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1277+ *
1278+ */
1279+
1280+#include "MockSessionsModel.h"
1281+#include <QLightDM/SessionsModel>
1282+
1283+QString MockSessionsModel::testScenario() const
1284+{
1285+ QLightDM::SessionsModel* qSessionsModel =
1286+ static_cast<QLightDM::SessionsModel*>(sourceModel());
1287+
1288+ return qSessionsModel->testScenario();
1289+}
1290+
1291+void MockSessionsModel::setTestScenario(const QString testScenario)
1292+{
1293+ QLightDM::SessionsModel* qSessionsModel =
1294+ static_cast<QLightDM::SessionsModel*>(sourceModel());
1295+
1296+ if (qSessionsModel->testScenario() != testScenario) {
1297+ qSessionsModel->setTestScenario(testScenario);
1298+ }
1299+}
1300
1301=== added file 'tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.h'
1302--- tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.h 1970-01-01 00:00:00 +0000
1303+++ tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.h 2016-06-30 19:05:07 +0000
1304@@ -0,0 +1,34 @@
1305+/*
1306+ * Copyright (C) 2015 Canonical, Ltd.
1307+ *
1308+ * This program is free software; you can redistribute it and/or modify
1309+ * it under the terms of the GNU General Public License as published by
1310+ * the Free Software Foundation; version 3.
1311+ *
1312+ * This program is distributed in the hope that it will be useful,
1313+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1314+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1315+ * GNU General Public License for more details.
1316+ *
1317+ * You should have received a copy of the GNU General Public License
1318+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1319+ *
1320+ */
1321+
1322+#ifndef MOCK_UNITY_SESSIONSMODEL_H
1323+#define MOCK_UNITY_SESSIONSMODEL_H
1324+
1325+#include <SessionsModel.h>
1326+
1327+class MockSessionsModel : public SessionsModel
1328+{
1329+ Q_OBJECT
1330+
1331+ Q_PROPERTY(QString testScenario READ testScenario WRITE setTestScenario)
1332+
1333+public:
1334+ QString testScenario() const;
1335+ void setTestScenario(QString testScenario);
1336+};
1337+
1338+#endif // MOCK_UNITY_SESSIONSMODEL_H
1339
1340=== added file 'tests/mocks/LightDM/IntegratedLightDM/QLightDM/SessionsModel'
1341--- tests/mocks/LightDM/IntegratedLightDM/QLightDM/SessionsModel 1970-01-01 00:00:00 +0000
1342+++ tests/mocks/LightDM/IntegratedLightDM/QLightDM/SessionsModel 2016-06-30 19:05:07 +0000
1343@@ -0,0 +1,17 @@
1344+/*
1345+ * Copyright (C) 2015 Canonical, Ltd.
1346+ *
1347+ * This program is free software; you can redistribute it and/or modify
1348+ * it under the terms of the GNU General Public License as published by
1349+ * the Free Software Foundation; version 3.
1350+ *
1351+ * This program is distributed in the hope that it will be useful,
1352+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1353+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1354+ * GNU General Public License for more details.
1355+ *
1356+ * You should have received a copy of the GNU General Public License
1357+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1358+ */
1359+
1360+#include "../liblightdm/SessionsModel.h"
1361
1362=== modified file 'tests/mocks/LightDM/IntegratedLightDM/liblightdm/CMakeLists.txt'
1363--- tests/mocks/LightDM/IntegratedLightDM/liblightdm/CMakeLists.txt 2016-06-30 19:05:01 +0000
1364+++ tests/mocks/LightDM/IntegratedLightDM/liblightdm/CMakeLists.txt 2016-06-30 19:05:07 +0000
1365@@ -1,7 +1,9 @@
1366 set(LibLightDM_SOURCES
1367 Greeter.cpp
1368+ SessionsModel.cpp
1369 UsersModel.cpp
1370 GreeterPrivate.cpp
1371+ SessionsModelPrivate.cpp
1372 UsersModelPrivate.cpp
1373 ${CMAKE_SOURCE_DIR}/plugins/Utils/qvariantlistmodel.cpp
1374 )
1375
1376=== modified file 'tests/mocks/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp'
1377--- tests/mocks/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp 2016-06-30 19:05:01 +0000
1378+++ tests/mocks/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp 2016-06-30 19:05:07 +0000
1379@@ -51,7 +51,7 @@
1380
1381 QString Greeter::defaultSessionHint() const
1382 {
1383- return "ubuntu";
1384+ return QStringLiteral("ubuntu");
1385 }
1386
1387 bool Greeter::hideUsersHint() const
1388
1389=== added file 'tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModel.cpp'
1390--- tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModel.cpp 1970-01-01 00:00:00 +0000
1391+++ tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModel.cpp 2016-06-30 19:05:07 +0000
1392@@ -0,0 +1,99 @@
1393+/*
1394+ * Copyright (C) 2015 Canonical, Ltd.
1395+ *
1396+ * This program is free software; you can redistribute it and/or modify
1397+ * it under the terms of the GNU General Public License as published by
1398+ * the Free Software Foundation; version 3.
1399+ *
1400+ * This program is distributed in the hope that it will be useful,
1401+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1402+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1403+ * GNU General Public License for more details.
1404+ *
1405+ * You should have received a copy of the GNU General Public License
1406+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1407+ *
1408+ */
1409+
1410+
1411+// LightDM currently is Qt4 compatible, and so doesn't define setRoleNames.
1412+// To use the same method of setting role name that it does, we
1413+// set our compatibility to Qt4 here too.
1414+#define QT_DISABLE_DEPRECATED_BEFORE QT_VERSION_CHECK(4, 0, 0)
1415+
1416+#include "SessionsModel.h"
1417+#include "SessionsModelPrivate.h"
1418+#include <QtCore/QDir>
1419+#include <QtCore/QString>
1420+
1421+namespace QLightDM
1422+{
1423+
1424+SessionsModel::SessionsModel(QObject* parent) :
1425+ QAbstractListModel(parent),
1426+ d_ptr(new SessionsModelPrivate(this))
1427+{
1428+ Q_D(SessionsModel);
1429+ m_roleNames = QAbstractListModel::roleNames();
1430+ m_roleNames[KeyRole] = "key";
1431+ m_roleNames[TypeRole] = "type";
1432+}
1433+
1434+SessionsModel::~SessionsModel()
1435+{
1436+ delete d_ptr;
1437+}
1438+
1439+QVariant SessionsModel::data(const QModelIndex& index, int role) const
1440+{
1441+ Q_D(const SessionsModel);
1442+
1443+ if(!index.isValid()) {
1444+ return QVariant();
1445+ }
1446+
1447+ int row = index.row();
1448+
1449+ switch (role) {
1450+ case QLightDM::SessionsModel::KeyRole:
1451+ return d->sessionItems[row].key;
1452+ case Qt::DisplayRole:
1453+ return d->sessionItems[row].name;
1454+ default:
1455+ return QVariant();
1456+ }
1457+}
1458+
1459+QHash<int, QByteArray> SessionsModel::roleNames() const
1460+{
1461+ return m_roleNames;
1462+}
1463+
1464+int SessionsModel::rowCount(const QModelIndex& parent) const
1465+{
1466+ Q_D(const SessionsModel);
1467+
1468+ if (parent.isValid()) {
1469+ return 0;
1470+ } else { // parent is root
1471+ return d->sessionItems.size();
1472+ }
1473+}
1474+
1475+QString SessionsModel::testScenario() const
1476+{
1477+ Q_D(const SessionsModel);
1478+ return d->testScenario;
1479+}
1480+
1481+void SessionsModel::setTestScenario(QString testScenario)
1482+{
1483+ Q_D(SessionsModel);
1484+
1485+ if (d->testScenario != testScenario) {
1486+ d->testScenario = testScenario;
1487+ d->resetEntries();
1488+ }
1489+}
1490+
1491+} // namespace QLightDM
1492
1493=== added file 'tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModel.h'
1494--- tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModel.h 1970-01-01 00:00:00 +0000
1495+++ tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModel.h 2016-06-30 19:05:07 +0000
1496@@ -0,0 +1,72 @@
1497+/*
1498+ * Copyright (C) 2015 Canonical, Ltd.
1499+ *
1500+ * This program is free software; you can redistribute it and/or modify
1501+ * it under the terms of the GNU General Public License as published by
1502+ * the Free Software Foundation; version 3.
1503+ *
1504+ * This program is distributed in the hope that it will be useful,
1505+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1506+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1507+ * GNU General Public License for more details.
1508+ *
1509+ * You should have received a copy of the GNU General Public License
1510+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1511+ *
1512+ */
1513+
1514+#ifndef UNITY_MOCK_SESSIONSMODEL_H
1515+#define UNITY_MOCK_SESSIONSMODEL_H
1516+
1517+#include <QtCore/QAbstractListModel>
1518+#include <QtCore/QString>
1519+
1520+namespace QLightDM
1521+{
1522+class SessionsModelPrivate;
1523+
1524+class Q_DECL_EXPORT SessionsModel : public QAbstractListModel
1525+ {
1526+ Q_OBJECT
1527+
1528+ Q_ENUMS(SessionModelRoles SessionType)
1529+
1530+ // Mock-only API for testing purposes
1531+ Q_PROPERTY(QString testScenario READ testScenario WRITE setTestScenario)
1532+
1533+ public:
1534+
1535+ enum SessionModelRoles {
1536+ //name is exposed as Qt::DisplayRole
1537+ //comment is exposed as Qt::TooltipRole
1538+ KeyRole = Qt::UserRole,
1539+ IdRole = KeyRole, /** Deprecated */
1540+ TypeRole
1541+ };
1542+
1543+ enum SessionType {
1544+ LocalSessions,
1545+ RemoteSessions
1546+ };
1547+
1548+ explicit SessionsModel(QObject* parent=nullptr); /** Deprecated. Loads local sessions*/
1549+ explicit SessionsModel(SessionsModel::SessionType, QObject* parent=nullptr);
1550+ virtual ~SessionsModel();
1551+
1552+ QHash<int, QByteArray> roleNames() const override;
1553+ int rowCount(const QModelIndex& parent) const override;
1554+ QVariant data(const QModelIndex& index, int role) const override;
1555+
1556+ QString testScenario() const;
1557+ void setTestScenario(QString testScenario);
1558+
1559+ protected:
1560+ SessionsModelPrivate* const d_ptr;
1561+
1562+ private:
1563+ QHash<int, QByteArray> m_roleNames;
1564+ Q_DECLARE_PRIVATE(SessionsModel)
1565+ };
1566+}
1567+
1568+#endif // UNITY_MOCK_SESSIONSMODEL_H
1569
1570=== added file 'tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.cpp'
1571--- tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.cpp 1970-01-01 00:00:00 +0000
1572+++ tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.cpp 2016-06-30 19:05:07 +0000
1573@@ -0,0 +1,73 @@
1574+/*
1575+ * Copyright (C) 2015 Canonical, Ltd.
1576+ *
1577+ * This program is free software; you can redistribute it and/or modify
1578+ * it under the terms of the GNU General Public License as published by
1579+ * the Free Software Foundation; version 3.
1580+ *
1581+ * This program is distributed in the hope that it will be useful,
1582+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1583+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1584+ * GNU General Public License for more details.
1585+ *
1586+ * You should have received a copy of the GNU General Public License
1587+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1588+ */
1589+
1590+#include "SessionsModel.h"
1591+#include "SessionsModelPrivate.h"
1592+
1593+namespace QLightDM
1594+{
1595+
1596+SessionsModelPrivate::SessionsModelPrivate(SessionsModel* parent)
1597+ : testScenario("singleSession")
1598+ , q_ptr(parent)
1599+{
1600+ resetEntries();
1601+}
1602+
1603+void SessionsModelPrivate::resetEntries()
1604+{
1605+ Q_Q(SessionsModel);
1606+
1607+ q->beginResetModel();
1608+ if (testScenario == "multipleSessions") {
1609+ resetEntries_multipleSessions();
1610+ } else if (testScenario == "noSessions") {
1611+ resetEntries_noSessions();
1612+ } else {
1613+ resetEntries_singleSession();
1614+ }
1615+ q->endResetModel();
1616+}
1617+
1618+void SessionsModelPrivate::resetEntries_multipleSessions()
1619+{
1620+ sessionItems =
1621+ {
1622+ {"ubuntu", "", "Ubuntu", ""},
1623+ {"ubuntu-2d", "", "Ubuntu 2D", ""},
1624+ {"gnome", "", "GNOME", ""},
1625+ {"gnome-classic", "", "GNOME Classic", ""},
1626+ {"gnome-flashback-compiz", "", "GNOME Flashback (Compiz)", ""},
1627+ {"gnome-flashback-metacity", "", "GNOME Flashback (Metacity)", ""},
1628+ {"gnome-wayland", "", "GNOME on Wayland", ""},
1629+ {"plasma", "", "Plasma", ""},
1630+ {"kde", "", "KDE" , ""},
1631+ {"xterm", "", "Recovery Console", ""},
1632+ {"", "", "Unknown?", ""}
1633+ };
1634+}
1635+
1636+void SessionsModelPrivate::resetEntries_noSessions()
1637+{
1638+ sessionItems = {};
1639+}
1640+
1641+void SessionsModelPrivate::resetEntries_singleSession()
1642+{
1643+ sessionItems = {{"ubuntu", "", "Ubuntu", ""}};
1644+}
1645+
1646+} // namespace QLightDM
1647
1648=== added file 'tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.h'
1649--- tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.h 1970-01-01 00:00:00 +0000
1650+++ tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.h 2016-06-30 19:05:07 +0000
1651@@ -0,0 +1,59 @@
1652+/*
1653+ * Copyright (C) 2015 Canonical, Ltd.
1654+ *
1655+ * This program is free software; you can redistribute it and/or modify
1656+ * it under the terms of the GNU General Public License as published by
1657+ * the Free Software Foundation; version 3.
1658+ *
1659+ * This program is distributed in the hope that it will be useful,
1660+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1661+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1662+ * GNU General Public License for more details.
1663+ *
1664+ * You should have received a copy of the GNU General Public License
1665+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1666+ *
1667+ */
1668+
1669+#ifndef UNITY_MOCK_SESSIONSMODEL_PRIVATE_H
1670+#define UNITY_MOCK_SESSIONSMODEL_PRIVATE_H
1671+
1672+#include <QtCore/QList>
1673+#include <QtCore/QString>
1674+
1675+namespace QLightDM
1676+{
1677+class SessionsModel;
1678+
1679+class SessionItem
1680+{
1681+public:
1682+ QString key;
1683+ QString type; // unused
1684+ QString name;
1685+ QString comment; // unused
1686+};
1687+
1688+class SessionsModelPrivate
1689+{
1690+public:
1691+ explicit SessionsModelPrivate(SessionsModel* parent=0);
1692+ virtual ~SessionsModelPrivate() = default;
1693+
1694+ QList<SessionItem> sessionItems;
1695+ QString testScenario;
1696+
1697+ void resetEntries();
1698+protected:
1699+ SessionsModel* const q_ptr;
1700+
1701+private:
1702+ void resetEntries_multipleSessions();
1703+ void resetEntries_noSessions();
1704+ void resetEntries_singleSession();
1705+ Q_DECLARE_PUBLIC(SessionsModel)
1706+};
1707+
1708+} // namespace QLightDM
1709+
1710+#endif // UNITY_MOCK_SESSIONSMODEL_PRIVATE_H
1711
1712=== modified file 'tests/mocks/LightDM/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp'
1713--- tests/mocks/LightDM/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp 2016-06-18 22:37:48 +0000
1714+++ tests/mocks/LightDM/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp 2016-06-30 19:05:07 +0000
1715@@ -60,7 +60,7 @@
1716 {
1717 entries =
1718 {
1719- { "single", "Single User", 0, 0, false, false, 0, 0, 0 },
1720+ { "single", "Single User", 0, 0, false, false, "ubuntu", 0 },
1721 };
1722 }
1723
1724@@ -68,7 +68,7 @@
1725 {
1726 entries =
1727 {
1728- { "single", "Single User", 0, 0, false, false, 0, 0, 0 },
1729+ { "single", "Single User", 0, 0, false, false, "ubuntu", 0 },
1730 };
1731 }
1732
1733@@ -76,7 +76,7 @@
1734 {
1735 entries =
1736 {
1737- { "has-pin", "Has PIN", 0, 0, false, false, 0, 0, 0 },
1738+ { "has-pin", "Has PIN", 0, 0, false, false, "ubuntu", 0 },
1739 };
1740 }
1741
1742@@ -84,26 +84,26 @@
1743 {
1744 entries =
1745 {
1746- { "has-password", "Has Password", 0, 0, false, false, 0, 0, 0 },
1747- { "has-pin", "Has PIN", 0, 0, false, false, 0, 0, 0 },
1748- { "different-prompt", "Different Prompt", 0, 0, false, false, 0, 0, 0 },
1749- { "no-password", "No Password", 0, 0, false, false, 0, 0, 0 },
1750- { "auth-error", "Auth Error", 0, 0, false, false, 0, 0, 0 },
1751- { "two-factor", "Two Factor", 0, 0, false, false, 0, 0, 0 },
1752- { "info-prompt", "Info Prompt", 0, 0, false, false, 0, 0, 0 },
1753- { "html-info-prompt", "HTML Info Prompt", 0, 0, false, false, 0, 0, 0 },
1754- { "long-info-prompt", "Long Info Prompt", 0, 0, false, false, 0, 0, 0 },
1755- { "wide-info-prompt", "Wide Info Prompt", 0, 0, false, false, 0, 0, 0 },
1756- { "multi-info-prompt", "Multi Info Prompt", 0, 0, false, false, 0, 0, 0 },
1757- { "long-name", "Long name (far far too long to fit, seriously this would never fit on the screen, you will never see this part of the name)", 0, 0, false, false, 0, 0, 0 },
1758- { "color-background", "Color Background", "#dd4814", 0, false, false, 0, 0, 0 },
1759+ { "has-password", "Has Password", 0, 0, false, false, "ubuntu", 0 },
1760+ { "has-pin", "Has PIN", 0, 0, false, false, "ubuntu", 0 },
1761+ { "different-prompt", "Different Prompt", 0, 0, false, false, "ubuntu", 0 },
1762+ { "no-password", "No Password", 0, 0, false, false, "ubuntu", 0 },
1763+ { "auth-error", "Auth Error", 0, 0, false, false, "ubuntu", 0 },
1764+ { "two-factor", "Two Factor", 0, 0, false, false, "ubuntu", 0 },
1765+ { "info-prompt", "Info Prompt", 0, 0, false, false, "ubuntu", 0 },
1766+ { "html-info-prompt", "HTML Info Prompt", 0, 0, false, false, "ubuntu", 0 },
1767+ { "long-info-prompt", "Long Info Prompt", 0, 0, false, false, "ubuntu", 0 },
1768+ { "wide-info-prompt", "Wide Info Prompt", 0, 0, false, false, "ubuntu", 0 },
1769+ { "multi-info-prompt", "Multi Info Prompt", 0, 0, false, false, "ubuntu", 0 },
1770+ { "long-name", "Long name (far far too long to fit, seriously this would never fit on the screen, you will never see this part of the name)", 0, 0, false, false, "ubuntu", 0 },
1771+ { "color-background", "Color Background", "#dd4814", 0, false, false, "ubuntu", 0 },
1772 // white and black are a bit redundant, but useful for manually testing if UI is still readable
1773- { "white-background", "White Background", "#ffffff", 0, false, false, 0, 0, 0 },
1774- { "black-background", "Black Background", "#000000", 0, false, false, 0, 0, 0 },
1775- { "no-background", "No Background", "", 0, false, false, 0, 0, 0 },
1776- { "unicode", "가나다라마", 0, 0, false, false, 0, 0, 0 },
1777- { "no-response", "No Response", 0, 0, false, false, 0, 0, 0 },
1778- { "empty-name", "", 0, 0, false, false, 0, 0, 0 },
1779+ { "white-background", "White Background", "#ffffff", 0, false, false, "ubuntu", 0 },
1780+ { "black-background", "Black Background", "#000000", 0, false, false, "ubuntu", 0 },
1781+ { "no-background", "No Background", "", 0, false, false, "ubuntu", 0 },
1782+ { "unicode", "가나다라마", 0, 0, false, false, "ubuntu", 0 },
1783+ { "no-response", "No Response", 0, 0, false, false, "ubuntu", 0 },
1784+ { "empty-name", "", 0, 0, false, false, "ubuntu", 0 },
1785 };
1786 }
1787
1788
1789=== modified file 'tests/mocks/LightDM/IntegratedLightDM/plugin.cpp'
1790--- tests/mocks/LightDM/IntegratedLightDM/plugin.cpp 2016-06-30 19:05:01 +0000
1791+++ tests/mocks/LightDM/IntegratedLightDM/plugin.cpp 2016-06-30 19:05:07 +0000
1792@@ -18,7 +18,9 @@
1793 #include <DBusGreeter.h>
1794 #include <DBusGreeterList.h>
1795 #include "MockGreeter.h"
1796+#include "MockSessionsModel.h"
1797 #include "MockUsersModel.h"
1798+#include <QLightDM/SessionsModel>
1799 #include "ColorTheme.h"
1800 #include "UserMetrics.h"
1801 #include <QLightDM/UsersModel>
1802@@ -39,6 +41,13 @@
1803 return greeter;
1804 }
1805
1806+static QObject *sessions_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
1807+{
1808+ Q_UNUSED(engine)
1809+ Q_UNUSED(scriptEngine)
1810+ return new MockSessionsModel;
1811+}
1812+
1813 static QObject *users_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
1814 {
1815 Q_UNUSED(engine)
1816@@ -62,6 +71,8 @@
1817 qRegisterMetaType<QLightDM::Greeter::MessageType>("QLightDM::Greeter::MessageType");
1818 qRegisterMetaType<QLightDM::Greeter::PromptType>("QLightDM::Greeter::PromptType");
1819 qmlRegisterSingletonType<MockGreeter>(uri, 0, 1, "Greeter", greeter_provider);
1820+ qmlRegisterSingletonType<MockSessionsModel>(uri, 0, 1, "Sessions", sessions_provider);
1821+ qmlRegisterUncreatableType<SessionsModel>(uri, 0, 1, "SessionRoles", "Type is not instantiable");
1822 qmlRegisterSingletonType<MockUsersModel>(uri, 0, 1, "Users", users_provider);
1823 qmlRegisterUncreatableType<QLightDM::UsersModel>(uri, 0, 1, "UserRoles", "Type is not instantiable");
1824 qmlRegisterSingletonType<UserMetricsOutput::UserMetrics>(uri, 0, 1, "Infographic", infographic_provider);
1825
1826=== modified file 'tests/plugins/LightDM/IntegratedLightDM/CMakeLists.txt'
1827--- tests/plugins/LightDM/IntegratedLightDM/CMakeLists.txt 2016-06-30 19:05:01 +0000
1828+++ tests/plugins/LightDM/IntegratedLightDM/CMakeLists.txt 2016-06-30 19:05:07 +0000
1829@@ -44,6 +44,29 @@
1830 )
1831 add_qmltest_target(testGreeterPam GreeterPamTestExec COMMAND $<TARGET_FILE:GreeterPamTestExec>)
1832
1833+# SessionsModelTest
1834+add_executable(GreeterSessionsModelTestExec
1835+ sessionsmodel.cpp
1836+ ${CMAKE_SOURCE_DIR}/plugins/LightDM/SessionsModel.cpp
1837+ ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
1838+ )
1839+add_dependencies(GreeterSessionsModelTestExec MockLightDM)
1840+qt5_use_modules(GreeterSessionsModelTestExec Core Test)
1841+target_link_libraries(GreeterSessionsModelTestExec
1842+ MockLightDM
1843+ )
1844+target_include_directories(GreeterSessionsModelTestExec PUBLIC
1845+ ${CMAKE_SOURCE_DIR}/plugins/LightDM
1846+ ${CMAKE_SOURCE_DIR}/plugins/Utils
1847+ ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM/IntegratedLightDM
1848+ )
1849+add_unity8_uitest(GreeterSessions GreeterSessionsModelTestExec
1850+ ENVIRONMENT LIBLIGHTDM_MOCK_MODE=full
1851+ DEPENDS MockLightDM
1852+ LIGHTDM
1853+ )
1854+
1855+# UsersModelTest
1856 add_executable(GreeterUsersModelTestExec
1857 usersmodel.cpp
1858 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
1859
1860=== added file 'tests/plugins/LightDM/IntegratedLightDM/sessionsmodel.cpp'
1861--- tests/plugins/LightDM/IntegratedLightDM/sessionsmodel.cpp 1970-01-01 00:00:00 +0000
1862+++ tests/plugins/LightDM/IntegratedLightDM/sessionsmodel.cpp 2016-06-30 19:05:07 +0000
1863@@ -0,0 +1,96 @@
1864+/*
1865+ * Copyright (C) 2015 Canonical, Ltd.
1866+ *
1867+ * This program is free software; you can redistribute it and/or modify
1868+ * it under the terms of the GNU General Public License as published by
1869+ * the Free Software Foundation; version 3.
1870+ *
1871+ * This program is distributed in the hope that it will be useful,
1872+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1873+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1874+ * GNU General Public License for more details.
1875+ *
1876+ * You should have received a copy of the GNU General Public License
1877+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1878+ */
1879+
1880+#include "SessionsModel.h"
1881+
1882+#include <QLightDM/SessionsModel>
1883+#include <QtCore/QModelIndex>
1884+#include <QtTest>
1885+#include <QString>
1886+
1887+class GreeterSessionsModelTest : public QObject
1888+{
1889+ Q_OBJECT
1890+
1891+private Q_SLOTS:
1892+
1893+ void init()
1894+ {
1895+ model = new SessionsModel();
1896+ QVERIFY(model);
1897+ sourceModel = new QLightDM::SessionsModel();
1898+ QVERIFY(sourceModel);
1899+ }
1900+
1901+ void cleanup()
1902+ {
1903+ delete model;
1904+ delete sourceModel;
1905+ }
1906+
1907+ static QModelIndex findByKey(QAbstractItemModel *model, const QString& key)
1908+ {
1909+ for (int i = 0; i < model->rowCount(QModelIndex()); i++) {
1910+ if (model->data(model->index(i, 0), QLightDM::SessionsModel::KeyRole).toString() == key)
1911+ return model->index(i, 0);
1912+ }
1913+
1914+ return QModelIndex();
1915+ }
1916+
1917+ void testIconDirectoriesAreValid()
1918+ {
1919+ Q_FOREACH(const QUrl& searchDirectory, model->iconSearchDirectories())
1920+ {
1921+ QVERIFY(searchDirectory.isValid());
1922+ }
1923+ }
1924+
1925+ void testMultipleSessionsCountIsCorrect()
1926+ {
1927+ sourceModel->setTestScenario("multipleSessions");
1928+ QVERIFY(sourceModel->rowCount(QModelIndex()) > 1);
1929+ }
1930+
1931+ void testNoSessionsCountIsCorrect()
1932+ {
1933+ sourceModel->setTestScenario("noSessions");
1934+ QVERIFY(sourceModel->rowCount(QModelIndex()) == 0);
1935+ }
1936+
1937+ void testSingleSessionCountIsCorrect()
1938+ {
1939+ sourceModel->setTestScenario("singleSession");
1940+ QVERIFY(sourceModel->rowCount(QModelIndex()) == 1);
1941+ }
1942+
1943+ void testSessionNameIsCorrect()
1944+ {
1945+ // This is testing the lookup, not the correctness of the strings,
1946+ // so one test should be sufficient
1947+ sourceModel->setTestScenario("multipleSessions");
1948+ QVERIFY(model->data(findByKey(sourceModel, "ubuntu"),
1949+ Qt::DisplayRole).toString() == "Ubuntu");
1950+ }
1951+
1952+private:
1953+ SessionsModel *model;
1954+ QLightDM::SessionsModel *sourceModel;
1955+};
1956+
1957+QTEST_MAIN(GreeterSessionsModelTest)
1958+
1959+#include "sessionsmodel.moc"
1960
1961=== modified file 'tests/qmltests/Greeter/TestView.qml'
1962--- tests/qmltests/Greeter/TestView.qml 2016-06-30 19:05:01 +0000
1963+++ tests/qmltests/Greeter/TestView.qml 2016-05-25 19:25:51 +0000
1964@@ -43,7 +43,6 @@
1965 signal responded(string response)
1966 signal tease()
1967 signal emergencyCall()
1968- signal promptlessLogin()
1969
1970 signal _showMessageCalled(string html)
1971 signal _showPromptCalled(string text, bool isSecret, bool isDefaultPrompt)
1972
1973=== modified file 'tests/qmltests/Greeter/tst_Greeter.qml'
1974--- tests/qmltests/Greeter/tst_Greeter.qml 2016-06-30 19:05:01 +0000
1975+++ tests/qmltests/Greeter/tst_Greeter.qml 2016-06-30 19:05:07 +0000
1976@@ -194,6 +194,8 @@
1977 var i = getIndexOf(name);
1978 view.selected(i);
1979 verifySelected(name);
1980+ compare(greeter.waiting, true);
1981+ tryCompare(greeter, "waiting", false);
1982 return i;
1983 }
1984
1985@@ -325,18 +327,6 @@
1986 compare(viewShowMessageSpy.signalArguments[2][0], "You should have seen three messages");
1987 }
1988
1989- function test_waiting() {
1990- // Make sure we unset 'waiting' on prompt
1991- selectUser("has-password");
1992- compare(greeter.waiting, true);
1993- tryCompare(greeter, "waiting", false);
1994-
1995- // Make sure we unset 'waiting' on authentication
1996- selectUser("no-password");
1997- compare(greeter.waiting, true);
1998- tryCompare(greeter, "waiting", false);
1999- }
2000-
2001 function test_locked() {
2002 selectUser("has-password");
2003 compare(view.locked, true);
2004
2005=== modified file 'tests/qmltests/Greeter/tst_WideView.qml'
2006--- tests/qmltests/Greeter/tst_WideView.qml 2016-06-30 19:05:01 +0000
2007+++ tests/qmltests/Greeter/tst_WideView.qml 2016-06-30 19:05:07 +0000
2008@@ -63,7 +63,8 @@
2009 }
2010
2011 onSelected: {
2012- currentIndexField.text = index;
2013+ if (index >= 0)
2014+ currentIndexField.text = index;
2015 }
2016
2017 QtObject {
2018@@ -392,7 +393,7 @@
2019
2020 function test_respondedWithPassword() {
2021 view.locked = true;
2022- view.showPrompt("Prompt", true, true);
2023+ view.showPrompt("Prompt", true, false);
2024 var passwordInput = findChild(view, "passwordInput");
2025 compare(passwordInput.text, "Prompt");
2026 verify(passwordInput.isSecret);
2027@@ -454,13 +455,13 @@
2028 view.showPrompt("Prompt", true, true);
2029 var promptField = findChild(view, "promptField");
2030 tap(promptField);
2031- compare(promptField.focus, true);
2032- compare(promptField.enabled, true);
2033+ verify(promptField.activeFocus);
2034+ compare(promptField.opacity, 1);
2035
2036 typeString("password");
2037 keyClick(Qt.Key_Enter);
2038- compare(promptField.focus, true);
2039- compare(promptField.enabled, false);
2040+ verify(promptField.activeFocus);
2041+ compare(promptField.opacity, 0); // hidden by fakeLabel
2042
2043 compare(selectedSpy.count, 0);
2044 keyClick(Qt.Key_Escape);
2045@@ -468,8 +469,8 @@
2046 compare(selectedSpy.signalArguments[0][0], 1);
2047
2048 view.reset();
2049- compare(promptField.focus, false);
2050- compare(promptField.enabled, true);
2051+ verify(promptField.activeFocus);
2052+ compare(promptField.opacity, 1);
2053 }
2054
2055 function test_unicode() {
2056@@ -489,6 +490,7 @@
2057 compare(selectedSpy.signalArguments[0][0], 0);
2058 selectedSpy.clear();
2059
2060+ view.reset();
2061 view.locked = false;
2062 compare(passwordInput.text, "Log In");
2063 tap(passwordInput);
2064@@ -518,13 +520,89 @@
2065 tryCompare(loginList, "height", view.height);
2066 }
2067
2068- function test_alphanumeric() {
2069- var passwordInput = findChild(view, "passwordInput");
2070+ function test_passphrase() {
2071+ var promptField = findChild(view, "promptField");
2072+ view.showPrompt("", true, true);
2073
2074 verify(view.alphanumeric);
2075- verify(passwordInput.isAlphanumeric);
2076+ compare(promptField.inputMethodHints & Qt.ImhDigitsOnly, 0);
2077+
2078+ keyClick(Qt.Key_D);
2079+ compare(promptField.text, "d");
2080+ }
2081+
2082+ function test_passcode() {
2083+ var promptField = findChild(view, "promptField");
2084+ view.showPrompt("", true, true);
2085+
2086 view.alphanumeric = false;
2087- verify(!passwordInput.isAlphanumeric);
2088+ compare(promptField.inputMethodHints & Qt.ImhDigitsOnly, Qt.ImhDigitsOnly);
2089+
2090+ keyClick(Qt.Key_D);
2091+ compare(promptField.text, "");
2092+
2093+ keyClick(Qt.Key_0);
2094+ keyClick(Qt.Key_0);
2095+ keyClick(Qt.Key_0);
2096+ keyClick(Qt.Key_0);
2097+ compare(promptField.text, "0000");
2098+
2099+ compare(respondedSpy.count, 1);
2100+ compare(respondedSpy.signalArguments[0][0], "0000");
2101+
2102+ compare(promptField.opacity, 0);
2103+ }
2104+
2105+ function test_loginListMovement_data() {
2106+ return [
2107+ {tag: "up", key: Qt.Key_Up, result: -1},
2108+ {tag: "down", key: Qt.Key_Down, result: 1},
2109+ ]
2110+ }
2111+
2112+ function test_loginListMovement(data) {
2113+ keyClick(data.key);
2114+ compare(selectedSpy.count, 1);
2115+ compare(selectedSpy.signalArguments[0][0], data.result);
2116+ }
2117+
2118+ function test_focusStaysActive() {
2119+ var promptField = findChild(view, "promptField");
2120+ var promptButton = findChild(view, "promptButton");
2121+
2122+ verify(promptButton.activeFocus);
2123+ keyClick(Qt.Key_Enter);
2124+ compare(selectedSpy.count, 0);
2125+ compare(respondedSpy.count, 1);
2126+ compare(respondedSpy.signalArguments[0][0], "");
2127+ verify(promptButton.activeFocus);
2128+ keyClick(Qt.Key_Enter);
2129+ compare(respondedSpy.count, 1);
2130+
2131+ view.showPrompt("", true, true);
2132+ verify(promptField.activeFocus);
2133+ keyClick(Qt.Key_D);
2134+ keyClick(Qt.Key_Enter);
2135+ compare(selectedSpy.count, 0);
2136+ compare(respondedSpy.count, 2);
2137+ compare(respondedSpy.signalArguments[1][0], "d");
2138+ verify(promptField.activeFocus);
2139+ keyClick(Qt.Key_Enter);
2140+ compare(respondedSpy.count, 2);
2141+
2142+ view.reset();
2143+ view.locked = true;
2144+ verify(promptButton.activeFocus);
2145+ keyClick(Qt.Key_Enter);
2146+ compare(respondedSpy.count, 2);
2147+ compare(selectedSpy.count, 1);
2148+ compare(selectedSpy.signalArguments[0][0], 0);
2149+ verify(promptButton.activeFocus);
2150+ keyClick(Qt.Key_Enter);
2151+ compare(selectedSpy.count, 1);
2152+
2153+ view.showPrompt("", true, true);
2154+ verify(promptField.activeFocus);
2155 }
2156 }
2157 }
2158
2159=== modified file 'tests/qmltests/Tutorial/tst_Tutorial.qml'
2160--- tests/qmltests/Tutorial/tst_Tutorial.qml 2016-06-30 19:05:01 +0000
2161+++ tests/qmltests/Tutorial/tst_Tutorial.qml 2016-06-30 19:05:07 +0000
2162@@ -296,7 +296,7 @@
2163 }
2164
2165 function prepareShell() {
2166- tryCompare(shell, "enabled", true); // enabled by greeter when ready
2167+ tryCompare(shell, "waitingOnGreeter", false); // reset by greeter when ready
2168
2169 WindowStateStorage.clear();
2170 SurfaceManager.inputMethodSurface.setState(Mir.MinimizedState);
2171
2172=== modified file 'tests/qmltests/tst_OrientedShell.qml'
2173--- tests/qmltests/tst_OrientedShell.qml 2016-06-30 19:05:01 +0000
2174+++ tests/qmltests/tst_OrientedShell.qml 2016-06-30 19:05:07 +0000
2175@@ -493,7 +493,7 @@
2176 }
2177
2178 function cleanup() {
2179- tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
2180+ tryCompare(shell, "waitingOnGreeter", false); // make sure greeter didn't leave us in disabled state
2181 shell = null;
2182 topLevelSurfaceList = null;
2183
2184@@ -1583,7 +1583,7 @@
2185 var stageLoader = findChild(shell, "applicationsDisplayLoader");
2186 verify(stageLoader);
2187
2188- tryCompare(shell, "enabled", true); // enabled by greeter when ready
2189+ tryCompare(shell, "waitingOnGreeter", false); // reset by greeter when ready
2190
2191 waitUntilShellIsInOrientation(root.physicalOrientation0);
2192
2193
2194=== modified file 'tests/qmltests/tst_Shell.qml'
2195--- tests/qmltests/tst_Shell.qml 2016-06-30 19:05:01 +0000
2196+++ tests/qmltests/tst_Shell.qml 2016-06-30 19:05:07 +0000
2197@@ -456,7 +456,7 @@
2198 function cleanup() {
2199 waitForRendering(shell);
2200 mouseEmulation.checked = true;
2201- tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
2202+ tryCompare(shell, "waitingOnGreeter", false); // make sure greeter didn't leave us in disabled state
2203 tearDown();
2204 WindowStateStorage.clear();
2205 }
2206@@ -466,7 +466,7 @@
2207 shellLoader.active = true;
2208 tryCompare(shellLoader, "status", Loader.Ready);
2209 removeTimeConstraintsFromSwipeAreas(shellLoader.item);
2210- tryCompare(shell, "enabled", true); // enabled by greeter when ready
2211+ tryCompare(shell, "waitingOnGreeter", false); // reset by greeter when ready
2212
2213 sessionSpy.target = findChild(shell, "greeter")
2214 dashCommunicatorSpy.target = findInvisibleChild(shell, "dashCommunicator");
2215@@ -818,7 +818,7 @@
2216 tryCompare(userlist, "currentIndex", next)
2217 tryCompare(userlist, "movingInternally", false)
2218 }
2219- tryCompare(shell, "enabled", true); // wait for PAM to settle
2220+ tryCompare(shell, "waitingOnGreeter", false); // wait for PAM to settle
2221 }
2222
2223 function selectUser(name) {
2224
2225=== modified file 'tests/qmltests/tst_ShellWithPin.qml'
2226--- tests/qmltests/tst_ShellWithPin.qml 2016-06-30 19:05:01 +0000
2227+++ tests/qmltests/tst_ShellWithPin.qml 2016-06-30 19:05:07 +0000
2228@@ -150,7 +150,7 @@
2229 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
2230
2231 function init() {
2232- tryCompare(shell, "enabled", true); // will be enabled when greeter is all ready
2233+ tryCompare(shell, "waitingOnGreeter", false); // will be set when greeter is all ready
2234 var greeter = findChild(shell, "greeter");
2235 sessionSpy.target = greeter;
2236 swipeAwayGreeter(true);
2237@@ -165,7 +165,7 @@
2238 }
2239
2240 function cleanup() {
2241- tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
2242+ tryCompare(shell, "waitingOnGreeter", false); // make sure greeter didn't leave us in disabled state
2243
2244 shellLoader.itemDestroyed = false
2245
2246@@ -533,10 +533,12 @@
2247
2248 // Confirm that we start disabled
2249 compare(promptSpy.count, 0);
2250- verify(!shell.enabled);
2251+ verify(shell.waitingOnGreeter);
2252+ var coverPageDragHandle = findChild(shell, "coverPageDragHandle");
2253+ verify(!coverPageDragHandle.enabled);
2254
2255 // And that we only become enabled once the lockscreen is up
2256- tryCompare(shell, "enabled", true);
2257+ tryCompare(shell, "waitingOnGreeter", false);
2258 verify(promptSpy.count > 0);
2259 var lockscreen = findChild(shell, "lockscreen");
2260 verify(lockscreen.shown);

Subscribers

People subscribed via source and target branches