on xenial help sign in
Merge greeter-apps
commit775438f18c2ef1f52827db0b77139038453ea83c authorMichael Terry <michael.terry@canonical.com> dateThu 11 Aug 2016, 13:55 (10 years ago) parentb4d4443f0c5036
30 files changed+737−114
plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp−5 plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.h−2 plugins/LightDM/SessionsModel.cpp+22−16 plugins/LightDM/plugin.cpp+1−1 qml/Greeter/FullLightDMImpl.qml+2 qml/Greeter/Greeter.qml+17−1 qml/Greeter/IntegratedLightDMImpl.qml+2−1 qml/Greeter/LightDMService.qml+2 qml/Greeter/LoginAreaContainer.qml+38 qml/Greeter/LoginList.qml+72−16 qml/Greeter/NarrowView.qml+1 qml/Greeter/SessionIcon.qml+47 qml/Greeter/SessionsList.qml+182 qml/Greeter/WideView.qml+102−39 qml/Greeter/graphics/session_icons/gnome_badge.png qml/Greeter/graphics/session_icons/kde_badge.png qml/Greeter/graphics/session_icons/recovery_console_badge.png qml/Greeter/graphics/session_icons/ubuntu_badge.png qml/Greeter/graphics/session_icons/unknown_badge.png tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.cpp+29 tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.h+12−2 tests/mocks/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp−5 tests/mocks/LightDM/IntegratedLightDM/liblightdm/Greeter.h−2 tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModel.cpp+22 tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModel.h+3−3 tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.cpp+27−17 tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.h+5−1 tests/mocks/LightDM/IntegratedLightDM/plugin.cpp+1−1 tests/plugins/LightDM/IntegratedLightDM/sessionsmodel.cpp+32 tests/qmltests/Greeter/tst_WideView.qml+118−2
plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp−5
@@ -78,11 +78,6 @@ bool Greeter::showRemoteLoginHint() const
7878 return true;
7979}
8080
81−bool Greeter::lockHint () const
82−{
83− return false;
84−}
85−
8681QString Greeter::selectUserHint() const
8782{
8883 return QLatin1String("");
plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.h−2
@@ -45,7 +45,6 @@ class Q_DECL_EXPORT Greeter : public QObject
4545
4646 Q_PROPERTY(QString hostname READ hostname CONSTANT)
4747 Q_PROPERTY(bool hasGuestAccount READ hasGuestAccountHint CONSTANT)
48− Q_PROPERTY(bool locked READ lockHint CONSTANT)
4948
5049 Q_ENUMS(PromptType MessageType)
5150
@@ -68,7 +67,6 @@ public:
6867 bool hideUsersHint() const;
6968 bool showManualLoginHint() const;
7069 bool showRemoteLoginHint() const;
71− bool lockHint () const;
7270 bool hasGuestAccountHint() const;
7371 QString selectUserHint() const;
7472 bool selectGuestHint() const;
plugins/LightDM/SessionsModel.cpp+22−16
@@ -47,15 +47,15 @@ void SessionsModel::setIconSearchDirectories(const QList<QUrl> searchDirectories
4747 Q_EMIT iconSearchDirectoriesChanged();
4848}
4949
50−QUrl SessionsModel::iconUrl(const QString sessionName) const
50+QUrl SessionsModel::iconUrl(const QString sessionKey) const
5151{
5252 Q_FOREACH(const QUrl& searchDirectory, m_iconSearchDirectories)
5353 {
5454 // This is an established icon naming convention
5555 QString customIconUrl = searchDirectory.toString(QUrl::StripTrailingSlash) +
56− "/custom_" + sessionName + "_badge.png";
56+ "/custom_" + sessionKey + "_badge.png";
5757 QString iconUrl = searchDirectory.toString(QUrl::StripTrailingSlash) +
58− "/" + sessionName + "_badge.png";
58+ "/" + sessionKey + "_badge.png";
5959
6060 QFile customIconFile(customIconUrl);
6161 QFile iconFile(iconUrl);
@@ -63,29 +63,35 @@ QUrl SessionsModel::iconUrl(const QString sessionName) const
6363 return QUrl(customIconUrl);
6464 } else if (iconFile.exists()) {
6565 return QUrl(iconUrl);
66− } else{
66+ } else {
6767 // Search the legacy way
6868 QString path = searchDirectory.toString(QUrl::StripTrailingSlash) + "/";
69− if (sessionName == "ubuntu" || sessionName == "ubuntu-2d") {
69+ bool iconFound = false;
70+ if (sessionKey == "ubuntu" || sessionKey == "ubuntu-2d") {
7071 path += "ubuntu_badge.png";
72+ iconFound = true;
7173 } else if(
72− sessionName == "gnome-classic" ||
73− sessionName == "gnome-flashback-compiz" ||
74− sessionName == "gnome-flashback-metacity" ||
75− sessionName == "gnome-shell" ||
76− sessionName == "gnome-wayland" ||
77− sessionName == "gnome"
74+ sessionKey == "gnome-classic" ||
75+ sessionKey == "gnome-flashback-compiz" ||
76+ sessionKey == "gnome-flashback-metacity" ||
77+ sessionKey == "gnome-shell" ||
78+ sessionKey == "gnome-wayland" ||
79+ sessionKey == "gnome"
7880 ){
7981 path += "gnome_badge.png";
80− } else if (sessionName == "plasma") {
82+ iconFound = true;
83+ } else if (sessionKey == "plasma") {
8184 path += "kde_badge.png";
82− } else if (sessionName == "xterm") {
85+ iconFound = true;
86+ } else if (sessionKey == "xterm") {
8387 path += "recovery_console_badge.png";
84− } else if (sessionName == "remote-login") {
88+ iconFound = true;
89+ } else if (sessionKey == "remote-login") {
8590 path += "remote_login_help.png";
91+ iconFound = true;
8692 }
8793
88− if (QFile(path).exists()) {
94+ if (QFile(path).exists() && iconFound) {
8995 return path;
9096 }
9197 }
@@ -99,7 +105,7 @@ QVariant SessionsModel::data(const QModelIndex& index, int role) const
99105{
100106 switch (role) {
101107 case SessionsModel::IconRole:
102− return iconUrl(m_model->data(index, Qt::DisplayRole).toString());
108+ return iconUrl(m_model->data(index, QLightDM::SessionsModel::KeyRole).toString());
103109 default:
104110 return m_model->data(index, role);
105111 }
plugins/LightDM/plugin.cpp+1−1
@@ -82,7 +82,7 @@ void PLUGIN_CLASSNAME::registerTypes(const char *uri)
8282 qRegisterMetaType<QLightDM::Greeter::PromptType>("QLightDM::Greeter::PromptType");
8383
8484 qmlRegisterSingletonType<SessionsModel>(uri, 0, 1, "Sessions", sessions_provider);
85− qmlRegisterUncreatableType<SessionsModel>(uri, 0, 1, "SessionRoles", QStringLiteral("Type is not instantiable"));
85+ qmlRegisterUncreatableType<QLightDM::SessionsModel>(uri, 0, 1, "SessionRoles", QStringLiteral("Type is not instantiable"));
8686
8787 qmlRegisterSingletonType<UsersModel>(uri, 0, 1, "Users", users_provider);
8888 qmlRegisterUncreatableType<QLightDM::UsersModel>(uri, 0, 1, "UserRoles", QStringLiteral("Type is not instantiable"));
qml/Greeter/FullLightDMImpl.qml+2
@@ -22,6 +22,8 @@ Item{
2222
2323 property var greeter: LightDM.Greeter
2424 property var infographic: LightDM.Infographic
25+ property var sessions: LightDM.Sessions
26+ property var sessionRoles: LightDM.SessionRoles
2527 property var users: LightDM.Users
2628 property var userRoles: LightDM.UserRoles
2729
qml/Greeter/Greeter.qml+17−1
@@ -132,6 +132,22 @@ Showable {
132132 return d.startUnlock(true /* toTheRight */);
133133 }
134134
135+ function sessionToStart() {
136+ for (var i = 0; i < LightDMService.sessions.count; i++) {
137+ var session = LightDMService.sessions.data(i,
138+ LightDMService.sessionRoles.KeyRole);
139+ if (loader.item.sessionToStart === session) {
140+ return session;
141+ }
142+ }
143+
144+ if (loader.item.sessionToStart === LightDMService.greeter.defaultSession) {
145+ return LightDMService.greeter.defaultSession;
146+ } else {
147+ return "ubuntu"; // The default / fallback
148+ }
149+ }
150+
135151 QtObject {
136152 id: d
137153
@@ -197,7 +213,7 @@ Showable {
197213
198214 function login() {
199215 d.waiting = true;
200− if (LightDMService.greeter.startSessionSync()) {
216+ if (LightDMService.greeter.startSessionSync(root.sessionToStart())) {
201217 sessionStarted();
202218 hideView();
203219 } else if (loader.item) {
qml/Greeter/IntegratedLightDMImpl.qml+2−1
@@ -22,7 +22,8 @@ Item{
2222
2323 property var greeter: LightDM.Greeter
2424 property var infographic: LightDM.Infographic
25+ property var sessions: LightDM.Sessions
26+ property var sessionRoles: LightDM.SessionRoles
2527 property var users: LightDM.Users
2628 property var userRoles: LightDM.UserRoles
27−
2829}
qml/Greeter/LightDMService.qml+2
@@ -27,6 +27,8 @@ Loader {
2727
2828 property var greeter: d.valid ? loader.item.greeter : null
2929 property var infographic: d.valid ? loader.item.infographic : null
30+ property var sessions: d.valid ? loader.item.sessions : null
31+ property var sessionRoles: d.valid ? loader.item.sessionRoles : null
3032 property var users: d.valid ? loader.item.users : null
3133 property var userRoles: d.valid ? loader.item.userRoles : null
3234
qml/Greeter/LoginAreaContainer.qmlnew file+38
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+
20+Item {
21+ BorderImage {
22+ anchors {
23+ fill: parent
24+ topMargin: -units.gu(1)
25+ leftMargin: -units.gu(1.5)
26+ rightMargin: -units.gu(1.5)
27+ bottomMargin: -units.gu(1.5)
28+ }
29+ source: "../Stages/graphics/dropshadow2gu.sci"
30+ opacity: 0.35
31+ }
32+
33+ UbuntuShape {
34+ anchors.fill: parent
35+ aspect: UbuntuShape.Flat
36+ backgroundColor: theme.palette.normal.raised
37+ }
38+}
qml/Greeter/LoginList.qml+72−16
@@ -18,6 +18,7 @@ import QtQuick 2.4
1818import QtGraphicalEffects 1.0
1919import Ubuntu.Components 1.3
2020import "../Components"
21+import "." 0.1
2122
2223StyledItem {
2324 id: root
@@ -29,15 +30,20 @@ StyledItem {
2930 property bool locked
3031 property bool waiting
3132
33+ readonly property alias passwordInput: passwordInput
3234 readonly property int numAboveBelow: 4
3335 readonly property int cellHeight: units.gu(5)
3436 readonly property int highlightedHeight: units.gu(15)
3537 readonly property int moveDuration: 200
38+ property string selectedSession
39+ property string currentSession
3640 readonly property string currentUser: userList.currentItem.username
3741 property bool wasPrompted: false
3842
39− signal selected(int index)
43+ signal loginListSessionChanged(string session)
4044 signal responded(string response)
45+ signal selected(int index)
46+ signal sessionChooserButtonClicked()
4147
4248 function tryToUnlock() {
4349 if (wasPrompted) {
@@ -97,6 +103,7 @@ StyledItem {
97103 name: "Ubuntu.Components.Themes.Ambiance"
98104 }
99105
106+ KeyNavigation.tab: sessionChooser
100107 Keys.onUpPressed: {
101108 selected(currentIndex - 1);
102109 event.accepted = true;
@@ -114,19 +121,7 @@ StyledItem {
114121 userList.currentIndex = currentIndex;
115122 }
116123
117− BorderImage {
118− anchors {
119− fill: highlightItem
120− topMargin: -units.gu(1)
121− leftMargin: -units.gu(1.5)
122− rightMargin: -units.gu(1.5)
123− bottomMargin: -units.gu(1.5)
124− }
125− source: "../Stages/graphics/dropshadow2gu.sci"
126− opacity: 0.35
127− }
128−
129− UbuntuShape {
124+ LoginAreaContainer {
130125 id: highlightItem
131126 anchors {
132127 left: parent.left
@@ -135,9 +130,8 @@ StyledItem {
135130 rightMargin: units.gu(2)
136131 verticalCenter: parent.verticalCenter
137132 }
133+
138134 height: root.highlightedHeight
139− aspect: UbuntuShape.Flat
140− backgroundColor: theme.palette.normal.raised
141135 }
142136
143137 ListView {
@@ -173,6 +167,7 @@ StyledItem {
173167
174168 readonly property bool belowHighlight: (userList.currentIndex < 0 && index > 0) || (userList.currentIndex >= 0 && index > userList.currentIndex)
175169 readonly property int belowOffset: root.highlightedHeight - root.cellHeight
170+ readonly property string userSession: session
176171 readonly property string username: name
177172
178173 opacity: {
@@ -237,6 +232,67 @@ StyledItem {
237232 }
238233 }
239234
235+ // Use an AbstractButton due to icon limitations with Button
236+ AbstractButton {
237+ id: sessionChooser
238+ objectName: "sessionChooserButton"
239+
240+ readonly property alias icon: badge.source
241+
242+ visible: LightDMService.sessions.count > 1 &&
243+ !LightDMService.users.data(userList.currentIndex, LightDMService.userRoles.LoggedInRole)
244+
245+ height: units.gu(3.5)
246+ width: units.gu(3.5)
247+
248+ anchors {
249+ right: highlightItem.right
250+ rightMargin: units.gu(2)
251+
252+ top: highlightItem.top
253+ topMargin: units.gu(1.5)
254+ }
255+
256+ Rectangle {
257+ id: badgeHighlight
258+
259+ anchors.fill: parent
260+ visible: parent.activeFocus
261+ color: "transparent"
262+ border.color: theme.palette.normal.focus
263+ border.width: units.dp(1)
264+ radius: 3
265+ }
266+
267+ Icon {
268+ id: badge
269+ anchors.fill: parent
270+ anchors.margins: units.dp(3)
271+ keyColor: "#ffffff" // icon providers give us white icons
272+ color: theme.palette.normal.raisedSecondaryText
273+ source: LightDMService.sessions.iconUrl(root.currentSession)
274+ }
275+
276+ KeyNavigation.tab: passwordInput
277+ Keys.onReturnPressed: {
278+ sessionChooserButtonClicked();
279+ event.accepted = true;
280+ }
281+
282+ onClicked: {
283+ sessionChooserButtonClicked();
284+ }
285+
286+ // Refresh the icon path if looking at different places at runtime
287+ // this is mainly for testing
288+ Connections {
289+ target: LightDMService.sessions
290+ onIconSearchDirectoriesChanged: {
291+ badge.source = LightDMService.sessions.iconUrl(root.currentSession)
292+ }
293+ }
294+ }
295+
240296 FadingLabel {
241297 id: infoLabel
242298 objectName: "infoLabel"
qml/Greeter/NarrowView.qml+1
@@ -31,6 +31,7 @@ FocusScope {
3131 property bool alphanumeric
3232 property var userModel // unused
3333 property alias infographicModel: coverPage.infographicModel
34+ property string sessionToStart
3435 property bool waiting
3536 readonly property bool fullyShown: coverPage.showProgress === 1 || lockscreen.shown
3637 readonly property bool required: coverPage.required || lockscreen.required
qml/Greeter/SessionIcon.qmlnew file+47
@@ -0,0 +1,47 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+
19+Image {
20+ id: root
21+
22+ property color color
23+ readonly property string fragColorShader:
24+ "
25+ varying highp vec2 qt_TexCoord0;
26+ uniform sampler2D source;
27+ uniform lowp vec4 color;
28+ uniform lowp float qt_Opacity;
29+
30+ void main()
31+ {
32+ lowp vec4 sourceColor = texture2D(source, qt_TexCoord0);
33+ gl_FragColor = color * sourceColor.a * qt_Opacity;
34+ }
35+ "
36+
37+ fillMode: Image.PreserveAspectFit
38+ height: sourceSize.height
39+
40+ ShaderEffect {
41+ readonly property color color: root.color
42+ readonly property Image source: parent
43+
44+ height: source.height; width: source.width
45+ fragmentShader: fragColorShader
46+ }
47+}
qml/Greeter/SessionsList.qmlnew file+182
@@ -0,0 +1,182 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import Ubuntu.Components.ListItems 1.3
20+import "." 0.1
21+import "../Components"
22+
23+Item {
24+ id: root
25+ objectName: "sessionsList"
26+
27+ property string initiallySelectedSession
28+ signal sessionSelected(string sessionKey)
29+ signal showLoginList()
30+
31+ onInitiallySelectedSessionChanged: {
32+ sessionsList.currentIndex = getSelectedIndex();
33+ sessionsList.positionViewAtIndex(sessionsList.currentIndex, ListView.Contain);
34+ }
35+
36+ function getSelectedIndex() {
37+ for (var i = 0; i < sessionsList.model.count; i++) {
38+ var key = sessionsList.model.get(i).key
39+ if (key === initiallySelectedSession) {
40+ return i;
41+ }
42+ }
43+ }
44+
45+ function currentKey() {
46+ var session = LightDMService.sessions.data(
47+ sessionsList.currentIndex, LightDMService.sessionRoles.KeyRole)
48+ return session;
49+ }
50+
51+ Keys.onEnterPressed: {
52+ showLoginList(); // Session is already selected
53+ event.accepted = true;
54+ }
55+
56+ Keys.onReturnPressed: {
57+ showLoginList();
58+ event.accepted = true;
59+ }
60+
61+ Keys.onDownPressed: {
62+ if (sessionsList.currentIndex < sessionsList.model.count - 1)
63+ sessionsList.currentIndex++;
64+ sessionSelected(currentKey());
65+ event.accepted = true;
66+ }
67+
68+ Keys.onUpPressed: {
69+ if (sessionsList.currentIndex > 0)
70+ sessionsList.currentIndex--;
71+ sessionSelected(currentKey());
72+ event.accepted = true;
73+ }
74+
75+ LoginAreaContainer {
76+ readonly property real margins: sessionsList.anchors.margins
77+ readonly property real prefferedHeight: {
78+ if (sessionsList.currentItem) {
79+ return ((sessionsList.currentItem.height + margins) *
80+ sessionsList.model.count) + sessionsList.headerItem.height + 2 * margins
81+ } else {
82+ return sessionsList.headerItem.height + 2 * margins
83+ }
84+ }
85+
86+ height: prefferedHeight < parent.height ? prefferedHeight : parent.height - units.gu(4)
87+ width: parent.width
88+
89+ anchors {
90+ left: parent.left
91+ right: parent.right
92+ verticalCenter: parent.verticalCenter
93+ }
94+
95+ UbuntuListView {
96+ id: sessionsList
97+
98+ anchors {
99+ top: parent.top
100+ left: parent.left
101+ right: parent.right
102+ margins: units.gu(2)
103+ }
104+
105+ height: parent.height - headerItem.height
106+
107+ boundsBehavior: Flickable.StopAtBounds
108+
109+ model: LightDMService.sessions
110+ header: ListItemLayout {
111+ id: header
112+
113+ padding.leading: 0 // handled by parent's margins
114+
115+ title.color: theme.palette.normal.raisedText
116+ title.font.pixelSize: units.gu(2.1)
117+ title.text: i18n.tr("Select desktop environment")
118+
119+ Icon {
120+ id: icon
121+ width: units.gu(3)
122+ SlotsLayout.position: SlotsLayout.Leading
123+ name: "go-previous"
124+
125+ MouseArea {
126+ anchors.fill: parent
127+ onClicked: showLoginList()
128+ }
129+ }
130+ }
131+
132+ headerPositioning: ListView.OverlayHeader
133+ highlightFollowsCurrentItem: false
134+
135+ delegate: ListItem {
136+ id: delegate
137+ objectName: "sessionDelegate" + index
138+
139+ divider.visible: false
140+
141+ visible: y > sessionsList.headerItem.y
142+ + sessionsList.headerItem.height
143+ - sessionsList.anchors.margins
144+
145+ MouseArea {
146+ anchors.fill: parent
147+ onClicked: {
148+ sessionsList.currentIndex = index
149+ sessionSelected(key)
150+ showLoginList()
151+ }
152+ }
153+
154+ Rectangle {
155+ height: parent.height
156+ width: parent.width
157+ color: "transparent"
158+ border {
159+ color: theme.palette.normal.positionText
160+ width: units.gu(0.2)
161+ }
162+
163+ visible: index === sessionsList.currentIndex
164+ }
165+
166+ ListItemLayout {
167+ id: layout
168+
169+ SessionIcon {
170+ id: sessionIcon
171+ source: icon_url
172+ SlotsLayout.position: SlotsLayout.Leading
173+ color: theme.palette.normal.raisedSecondaryText
174+ }
175+
176+ title.text: display
177+ title.color: theme.palette.normal.raisedText
178+ }
179+ }
180+ }
181+ }
182+}
qml/Greeter/WideView.qml+102−39
@@ -16,25 +16,27 @@
1616
1717import QtQuick 2.4
1818import Ubuntu.Components 1.3
19+import "." 0.1
1920
2021FocusScope {
2122 id: root
2223 focus: true
2324
25+ property alias background: coverPage.background
26+ property alias backgroundTopMargin: coverPage.backgroundTopMargin
2427 property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin
28+ property alias infographicModel: coverPage.infographicModel
2529 property alias launcherOffset: coverPage.launcherOffset
2630 property alias currentIndex: loginList.currentIndex
2731 property int delayMinutes // TODO
28− property alias backgroundTopMargin: coverPage.backgroundTopMargin
29− property alias background: coverPage.background
30− property bool locked
3132 property alias alphanumeric: loginList.alphanumeric
32− property alias userModel: loginList.model
33− property alias infographicModel: coverPage.infographicModel
34− property bool waiting
33+ property alias locked: loginList.locked
34+ property alias sessionToStart: loginList.currentSession
35+ property var userModel // Set from outside
36+
37+ readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
3538 readonly property bool fullyShown: coverPage.showProgress === 1
3639 readonly property bool required: coverPage.required
37− readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
3840
3941 // so that it can be replaced in tests with a mock object
4042 property var inputMethod: Qt.inputMethod
@@ -44,38 +46,22 @@ FocusScope {
4446 signal tease()
4547 signal emergencyCall() // unused
4648
47− function showMessage(html) {
48− loginList.showMessage(html);
49− }
50−
51− function showPrompt(text, isSecret, isDefaultPrompt) {
52− loginList.showPrompt(text, isSecret, isDefaultPrompt);
53− }
54−
55− function showLastChance() {
56− // TODO
57− }
58−
59− function hide() {
60− coverPage.hide();
61− }
62−
63− function notifyAuthenticationSucceeded(showFakePassword) {
64− // Nothing needed
65− }
66−
6749 function notifyAuthenticationFailed() {
6850 loginList.showError();
6951 }
7052
71− function showErrorMessage(msg) {
72− coverPage.showErrorMessage(msg);
73− }
74−
7553 function reset() {
7654 loginList.reset();
7755 }
7856
57+ function showMessage(html) {
58+ loginList.showMessage(html);
59+ }
60+
61+ function showPrompt(text, isSecret, isDefaultPrompt) {
62+ loginList.showPrompt(text, isSecret, isDefaultPrompt);
63+ }
64+
7965 function tryToUnlock(toTheRight) {
8066 if (root.locked) {
8167 coverPage.show();
@@ -92,6 +78,18 @@ FocusScope {
9278 }
9379 }
9480
81+ function hide() {
82+ coverPage.hide();
83+ }
84+
85+ function notifyAuthenticationSucceeded() {
86+ // Nothing needed
87+ }
88+
89+ function showLastChance() {
90+ // TODO
91+ }
92+
9593 Rectangle {
9694 anchors.fill: parent
9795 color: "black"
@@ -104,6 +102,7 @@ FocusScope {
104102 height: parent.height
105103 width: parent.width
106104 draggable: !root.locked && !root.waiting
105+ state: "LoginList"
107106
108107 infographics {
109108 height: 0.75 * parent.height
@@ -122,21 +121,85 @@ FocusScope {
122121 id: loginList
123122 objectName: "loginList"
124123
124+ height: inputMethod && inputMethod.visible ?
125+ parent.height - inputMethod.keyboardRectangle.height : parent.height
126+ width: units.gu(40)
125127 anchors {
126128 left: parent.left
127129 leftMargin: Math.min(parent.width * 0.16, units.gu(20))
128130 top: parent.top
129131 }
130− width: units.gu(40)
131− height: inputMethod && inputMethod.visible ? parent.height - inputMethod.keyboardRectangle.height
132− : parent.height
133− Behavior on height { UbuntuNumberAnimation {} }
134−
135− locked: root.locked
136− waiting: root.waiting
137132
138− onSelected: root.selected(index)
133+ model: root.userModel
134+ currentSession: LightDMService.greeter.defaultSession
139135 onResponded: root.responded(response)
136+ onSelected: root.selected(index)
137+ onSessionChooserButtonClicked: parent.state = "SessionsList"
138+
139+ Keys.forwardTo: [sessionChooserLoader.item]
140140 }
141+
142+ Loader {
143+ id: sessionChooserLoader
144+
145+ height: loginList.height
146+ width: loginList.width
147+ anchors {
148+ left: parent.left
149+ leftMargin: Math.min(parent.width * 0.16, units.gu(20))
150+ top: parent.top
151+ }
152+
153+ active: false
154+
155+ onLoaded: sessionChooserLoader.item.forceActiveFocus();
156+ Binding {
157+ target: sessionChooserLoader.item
158+ property: "initiallySelectedSession"
159+ value: loginList.currentSession
160+ }
161+
162+ Connections {
163+ target: sessionChooserLoader.item
164+ onSessionSelected: loginList.currentSession = sessionKey
165+ onShowLoginList: {
166+ coverPage.state = "LoginList"
167+ loginList.passwordInput.forceActiveFocus();
168+ }
169+ ignoreUnknownSignals: true
170+ }
171+ }
172+
173+ states: [
174+ State {
175+ name: "SessionsList"
176+ PropertyChanges { target: loginList; opacity: 0 }
177+ PropertyChanges { target: sessionChooserLoader;
178+ active: true;
179+ opacity: 1
180+ source: "SessionsList.qml"
181+ }
182+ },
183+
184+ State {
185+ name: "LoginList"
186+ PropertyChanges { target: loginList; opacity: 1 }
187+ PropertyChanges { target: sessionChooserLoader;
188+ active: false;
189+ opacity: 0
190+ source: "";
191+ }
192+ }
193+ ]
194+
195+ transitions: [
196+ Transition {
197+ from: "*"
198+ to: "*"
199+ UbuntuNumberAnimation {
200+ property: "opacity";
201+ }
202+ }
203+ ]
141204 }
142205}
qml/Greeter/graphics/session_icons/gnome_badge.pngnew file

Binary file not shown.

qml/Greeter/graphics/session_icons/kde_badge.pngnew file

Binary file not shown.

qml/Greeter/graphics/session_icons/recovery_console_badge.pngnew file

Binary file not shown.

qml/Greeter/graphics/session_icons/ubuntu_badge.pngnew file

Binary file not shown.

qml/Greeter/graphics/session_icons/unknown_badge.pngnew file

Binary file not shown.

tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.cpp+29
@@ -18,6 +18,23 @@
1818#include "MockSessionsModel.h"
1919#include <QLightDM/SessionsModel>
2020
21+
22+int MockSessionsModel::numSessions() const
23+{
24+ QLightDM::SessionsModel* qSessionsModel =
25+ static_cast<QLightDM::SessionsModel*>(sourceModel());
26+
27+ return qSessionsModel->numSessions();
28+}
29+
30+int MockSessionsModel::numAvailableSessions() const
31+{
32+ QLightDM::SessionsModel* qSessionsModel =
33+ static_cast<QLightDM::SessionsModel*>(sourceModel());
34+
35+ return qSessionsModel->numAvailableSessions();
36+}
37+
2138QString MockSessionsModel::testScenario() const
2239{
2340 QLightDM::SessionsModel* qSessionsModel =
@@ -26,6 +43,17 @@ QString MockSessionsModel::testScenario() const
2643 return qSessionsModel->testScenario();
2744}
2845
46+void MockSessionsModel::setNumSessions(const int numSessions)
47+{
48+ QLightDM::SessionsModel* qSessionsModel =
49+ static_cast<QLightDM::SessionsModel*>(sourceModel());
50+
51+ if (qSessionsModel->numSessions() != numSessions) {
52+ qSessionsModel->setNumSessions(numSessions);
53+ Q_EMIT numSessionsChanged();
54+ }
55+}
56+
2957void MockSessionsModel::setTestScenario(const QString testScenario)
3058{
3159 QLightDM::SessionsModel* qSessionsModel =
@@ -33,5 +61,6 @@ void MockSessionsModel::setTestScenario(const QString testScenario)
3361
3462 if (qSessionsModel->testScenario() != testScenario) {
3563 qSessionsModel->setTestScenario(testScenario);
64+ Q_EMIT testScenarioChanged();
3665 }
3766}
tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.h+12−2
@@ -24,11 +24,21 @@ class MockSessionsModel : public SessionsModel
2424{
2525 Q_OBJECT
2626
27− Q_PROPERTY(QString testScenario READ testScenario WRITE setTestScenario)
27+ Q_PROPERTY(int numAvailableSessions READ numAvailableSessions CONSTANT)
28+ Q_PROPERTY(int numSessions READ numSessions WRITE setNumSessions NOTIFY numSessionsChanged)
29+ Q_PROPERTY(QString testScenario READ testScenario WRITE setTestScenario NOTIFY testScenarioChanged)
2830
2931public:
32+ int numAvailableSessions() const;
33+ int numSessions() const;
3034 QString testScenario() const;
31− void setTestScenario(QString testScenario);
35+ void setNumSessions(const int numSessions);
36+ void setTestScenario(const QString testScenario);
37+
38+Q_SIGNALS:
39+ void numSessionsChanged();
40+ void testScenarioChanged();
41+
3242};
3343
3444#endif // MOCK_UNITY_SESSIONSMODEL_H
tests/mocks/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp−5
@@ -69,11 +69,6 @@ bool Greeter::showRemoteLoginHint() const
6969 return true;
7070}
7171
72−bool Greeter::lockHint () const
73−{
74− return false;
75−}
76−
7772QString Greeter::selectUserHint() const
7873{
7974 Q_D(const Greeter);
tests/mocks/LightDM/IntegratedLightDM/liblightdm/Greeter.h−2
@@ -41,7 +41,6 @@ class Q_DECL_EXPORT Greeter : public QObject
4141
4242 Q_PROPERTY(QString hostname READ hostname CONSTANT)
4343 Q_PROPERTY(bool hasGuestAccount READ hasGuestAccountHint CONSTANT)
44− Q_PROPERTY(bool locked READ lockHint CONSTANT)
4544
4645 Q_ENUMS(PromptType MessageType)
4746
@@ -67,7 +66,6 @@ public:
6766 bool hideUsersHint() const;
6867 bool showManualLoginHint() const;
6968 bool showRemoteLoginHint() const;
70− bool lockHint () const;
7169 bool hasGuestAccountHint() const;
7270 QString selectUserHint() const;
7371 void setSelectUserHint(const QString &selectUserHint); // only in mock
tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModel.cpp+22
@@ -80,12 +80,34 @@ int SessionsModel::rowCount(const QModelIndex& parent) const
8080 }
8181}
8282
83+int SessionsModel::numSessions() const
84+{
85+ Q_D(const SessionsModel);
86+ return d->numSessions;
87+}
88+
89+int SessionsModel::numAvailableSessions() const
90+{
91+ Q_D(const SessionsModel);
92+ return d->numAvailableSessions();
93+}
94+
8395QString SessionsModel::testScenario() const
8496{
8597 Q_D(const SessionsModel);
8698 return d->testScenario;
8799}
88100
101+void SessionsModel::setNumSessions(int numSessions)
102+{
103+ Q_D(SessionsModel);
104+
105+ if (d->numSessions != numSessions) {
106+ d->numSessions = numSessions;
107+ d->resetEntries();
108+ }
109+}
110+
89111void SessionsModel::setTestScenario(QString testScenario)
90112{
91113 Q_D(SessionsModel);
tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModel.h+3−3
@@ -31,9 +31,6 @@ class Q_DECL_EXPORT SessionsModel : public QAbstractListModel
3131
3232 Q_ENUMS(SessionModelRoles SessionType)
3333
34− // Mock-only API for testing purposes
35− Q_PROPERTY(QString testScenario READ testScenario WRITE setTestScenario)
36−
3734 public:
3835
3936 enum SessionModelRoles {
@@ -57,7 +54,10 @@ class Q_DECL_EXPORT SessionsModel : public QAbstractListModel
5754 int rowCount(const QModelIndex& parent) const override;
5855 QVariant data(const QModelIndex& index, int role) const override;
5956
57+ int numSessions() const;
58+ int numAvailableSessions() const;
6059 QString testScenario() const;
60+ void setNumSessions(int numSessions);
6161 void setTestScenario(QString testScenario);
6262
6363 protected:
tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.cpp+27−17
@@ -16,24 +16,44 @@
1616
1717#include "SessionsModel.h"
1818#include "SessionsModelPrivate.h"
19−
19+#include <QDebug>
2020namespace QLightDM
2121{
2222
2323SessionsModelPrivate::SessionsModelPrivate(SessionsModel* parent)
2424 : testScenario("singleSession")
25+ , m_availableSessions(
26+ {
27+ {"ubuntu", "", "Ubuntu", ""},
28+ {"ubuntu-2d", "", "Ubuntu 2D", ""},
29+ {"gnome", "", "GNOME", ""},
30+ {"gnome-classic", "", "GNOME Classic", ""},
31+ {"gnome-flashback-compiz", "", "GNOME Flashback (Compiz)", ""},
32+ {"gnome-flashback-metacity", "", "GNOME Flashback (Metacity)", ""},
33+ {"gnome-wayland", "", "GNOME on Wayland", ""},
34+ {"plasma", "", "Plasma", ""},
35+ {"kde", "", "KDE" , ""},
36+ {"xterm", "", "Recovery Console", ""},
37+ {"", "", "Unknown?", ""}
38+ })
2539 , q_ptr(parent)
2640{
41+ numSessions = numAvailableSessions();
2742 resetEntries();
2843}
2944
45+int SessionsModelPrivate::numAvailableSessions() const
46+{
47+ return m_availableSessions.length();
48+}
49+
3050void SessionsModelPrivate::resetEntries()
3151{
3252 Q_Q(SessionsModel);
3353
3454 q->beginResetModel();
3555 if (testScenario == "multipleSessions") {
36− resetEntries_multipleSessions();
56+ resetEntries_multipleSessions(numSessions);
3757 } else if (testScenario == "noSessions") {
3858 resetEntries_noSessions();
3959 } else {
@@ -42,22 +62,12 @@ void SessionsModelPrivate::resetEntries()
4262 q->endResetModel();
4363}
4464
45−void SessionsModelPrivate::resetEntries_multipleSessions()
65+void SessionsModelPrivate::resetEntries_multipleSessions(int numSessions)
4666{
47− sessionItems =
48− {
49− {"ubuntu", "", "Ubuntu", ""},
50− {"ubuntu-2d", "", "Ubuntu 2D", ""},
51− {"gnome", "", "GNOME", ""},
52− {"gnome-classic", "", "GNOME Classic", ""},
53− {"gnome-flashback-compiz", "", "GNOME Flashback (Compiz)", ""},
54− {"gnome-flashback-metacity", "", "GNOME Flashback (Metacity)", ""},
55− {"gnome-wayland", "", "GNOME on Wayland", ""},
56− {"plasma", "", "Plasma", ""},
57− {"kde", "", "KDE" , ""},
58− {"xterm", "", "Recovery Console", ""},
59− {"", "", "Unknown?", ""}
60− };
67+ sessionItems.clear();
68+ for (int i = 0; i < numSessions; i++) {
69+ sessionItems.append(m_availableSessions.value(i));
70+ }
6171}
6272
6373void SessionsModelPrivate::resetEntries_noSessions()
tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.h+5−1
@@ -40,7 +40,10 @@ public:
4040 explicit SessionsModelPrivate(SessionsModel* parent=0);
4141 virtual ~SessionsModelPrivate() = default;
4242
43+ int numAvailableSessions() const;
44+
4345 QList<SessionItem> sessionItems;
46+ int numSessions;
4447 QString testScenario;
4548
4649 void resetEntries();
@@ -48,7 +51,8 @@ protected:
4851 SessionsModel* const q_ptr;
4952
5053private:
51− void resetEntries_multipleSessions();
54+ const QList<SessionItem> m_availableSessions;
55+ void resetEntries_multipleSessions(int numSessions);
5256 void resetEntries_noSessions();
5357 void resetEntries_singleSession();
5458 Q_DECLARE_PUBLIC(SessionsModel)
tests/mocks/LightDM/IntegratedLightDM/plugin.cpp+1−1
@@ -72,7 +72,7 @@ void LightDMPlugin::registerTypes(const char *uri)
7272 qRegisterMetaType<QLightDM::Greeter::PromptType>("QLightDM::Greeter::PromptType");
7373 qmlRegisterSingletonType<MockGreeter>(uri, 0, 1, "Greeter", greeter_provider);
7474 qmlRegisterSingletonType<MockSessionsModel>(uri, 0, 1, "Sessions", sessions_provider);
75− qmlRegisterUncreatableType<SessionsModel>(uri, 0, 1, "SessionRoles", "Type is not instantiable");
75+ qmlRegisterUncreatableType<QLightDM::SessionsModel>(uri, 0, 1, "SessionRoles", "Type is not instantiable");
7676 qmlRegisterSingletonType<MockUsersModel>(uri, 0, 1, "Users", users_provider);
7777 qmlRegisterUncreatableType<QLightDM::UsersModel>(uri, 0, 1, "UserRoles", "Type is not instantiable");
7878 qmlRegisterSingletonType<UserMetricsOutput::UserMetrics>(uri, 0, 1, "Infographic", infographic_provider);
tests/plugins/LightDM/IntegratedLightDM/sessionsmodel.cpp+32
@@ -59,6 +59,38 @@ private Q_SLOTS:
5959 }
6060 }
6161
62+ void testIconLookupLogic()
63+ {
64+ const QString data[][2] = {
65+ //{SESSION KEY, ICON NAME}
66+ {"gnome-classic", "gnome_badge.png"},
67+ {"gnome-flashback-compiz", "gnome_badge.png"},
68+ {"gnome-flashback-metacity", "gnome_badge.png"},
69+ {"gnome-shell", "gnome_badge.png"},
70+ {"gnome-wayland", "gnome_badge.png"},
71+ {"gnome", "gnome_badge.png"},
72+ {"kde", "kde_badge.png"},
73+ {"plasma", "kde_badge.png"},
74+ {"recovery_console", "recovery_console_badge.png"},
75+ {"ubuntu","ubuntu_badge.png"},
76+ {"ubuntu-2d", "ubuntu_badge.png"},
77+ {"xterm", "recovery_console_badge.png"},
78+ {"made up session", "unknown_badge.png"},
79+ {"", "unknown_badge.png"}
80+ };
81+
82+ const short length = sizeof(data) / sizeof(data[0]);
83+ for (int i = 0; i < length; i++) {
84+ std::string key = data[i][0].toStdString();
85+ std::string icon = data[i][1].toStdString();
86+ std::string url = model->iconUrl(data[i][0]).toString().toStdString();
87+ std::string errorMessage = "Session icon url for " + key +
88+ " should contain " + icon + " but " + url + " was returned";
89+ QVERIFY2(model->iconUrl(data[i][0]).toString().contains(data[i][1]),
90+ errorMessage.c_str());
91+ }
92+ }
93+
6294 void testMultipleSessionsCountIsCorrect()
6395 {
6496 sourceModel->setTestScenario("multipleSessions");
tests/qmltests/Greeter/tst_WideView.qml+118−2
@@ -24,6 +24,7 @@ import Unity.Test 0.1 as UT
2424
2525StyledItem {
2626 id: root
27+
2728 width: units.gu(120)
2829 height: units.gu(80)
2930 focus: true
@@ -111,11 +112,9 @@ StyledItem {
111112 color: theme.palette.normal.background
112113 width: units.gu(40)
113114 height: parent.height
114−
115115 Column {
116116 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
117117 spacing: units.gu(1)
118−
119118 Row {
120119 Button {
121120 text: "Show Last Chance"
@@ -266,6 +265,51 @@ StyledItem {
266265 text: "emergency: " + emergencySpy.count
267266 }
268267 }
268+ Row {
269+ id: multipleSessions
270+ CheckBox {
271+ id: multipleSessionsCheckbox
272+ onClicked: {
273+ if (checked) {
274+ LightDM.Sessions.testScenario = "multipleSessions"
275+ } else {
276+ LightDM.Sessions.testScenario = "singleSession"
277+ }
278+ }
279+ Connections {
280+ target: LightDM.Sessions
281+ onTestScenarioChanged: {
282+ if (LightDM.Sessions.testScenario === "multipleSessions") {
283+ multipleSessionsCheckbox.checked = true;
284+ } else {
285+ multipleSessionsCheckbox.checked = false;
286+ }
287+ }
288+ }
289+ }
290+ Label {
291+ text: "Multiple Sessions"
292+ }
293+ }
294+ Row {
295+ Slider {
296+ id: numSessionsSlider
297+
298+ width: units.gu(10)
299+ minimumValue: 0
300+ maximumValue: LightDM.Sessions.numAvailableSessions
301+ value: LightDM.Sessions.numSessions
302+ visible: LightDM.Sessions.testScenario === "multipleSessions"
303+ Binding {
304+ target: LightDM.Sessions
305+ property: "numSessions"
306+ value: numSessionsSlider.value
307+ }
308+ }
309+ Label {
310+ text: "Available Sessions"
311+ }
312+ }
269313 Row {
270314 Button {
271315 text: "Reload View"
@@ -316,6 +360,7 @@ StyledItem {
316360 when: windowShown
317361
318362 property Item view: loader.status === Loader.Ready ? loader.item : null
363+ property url testIconDirectory: Qt.resolvedUrl("../../../qml/Greeter/graphics/session_icons")
319364
320365 function init() {
321366 selectIndex(0); // break binding with text field
@@ -323,6 +368,7 @@ StyledItem {
323368 respondedSpy.clear();
324369 teaseSpy.clear();
325370 emergencySpy.clear();
371+ LightDM.Sessions.testScenario = "multipleSessions"
326372 }
327373
328374 function cleanup() {
@@ -378,6 +424,76 @@ StyledItem {
378424 {tag: "right", x: view.width, offset: 0, count: 1, locked: false},
379425 ]
380426 }
427+
428+ function test_sessionIconsAreValid() {
429+ LightDM.Sessions.testScenario = "multipleSessions"
430+ var originalDirectories = LightDM.Sessions.iconSearchDirectories
431+ LightDM.Sessions.iconSearchDirectories = [testIconDirectory]
432+
433+ // Test the login list icon is valid
434+ var sessionChooserButton = findChild(view, "sessionChooserButton");
435+ compare(sessionChooserButton.visible, true);
436+
437+ var session = String(view.sessionToStart).toLowerCase();
438+ var icon = String(sessionChooserButton.icon);
439+ compare(icon.indexOf(session) > -1, true);
440+
441+ // Test the session list icons are valid
442+ var coverPage = findChild(view, "coverPage");
443+ coverPage.state = "SessionsList"
444+ var sessionsList = findChild(view, "sessionsList");
445+ tryCompare(sessionsList, "visible", true);
446+ }
447+
448+ function test_choosingNewSessionChangesLoginListIcon() {
449+ // Ensure the default session is selected (Ubuntu)
450+ loader.active = false;
451+ loader.active = true;
452+
453+ LightDM.Sessions.testScenario = "multipleSessions";
454+ var sessionChooserButton = findChild(view, "sessionChooserButton");
455+ var icon = String(sessionChooserButton.icon);
456+ compare(icon.indexOf("ubuntu") > -1, true);
457+
458+ tap(sessionChooserButton)
459+ for(var i = 0; i < LightDM.Sessions.count; i++) {
460+ var delegateName = "sessionDelegate" + String(i);
461+ var currentDelegate = findChild(view, delegateName);
462+ var sessionKey = LightDM.Sessions.data(i,LightDM.SessionRoles.KeyRole);
463+ if (sessionKey === "gnome-classic") {
464+ tap(currentDelegate);
465+ var sessionChooserButton = findChild(view, "sessionChooserButton");
466+ waitForRendering(sessionChooserButton);
467+ var icon = String(sessionChooserButton.icon);
468+ break;
469+ }
470+ }
471+
472+ compare(icon.indexOf("gnome") > -1, true,
473+ "Expected icon to contain gnome but it was " + icon);
474+ }
475+
476+ function test_noSessionsDoesntBreakView() {
477+ LightDM.Sessions.testScenario = "noSessions"
478+ compare(LightDM.Sessions.count, 0)
479+ }
480+
481+ function test_sessionIconNotShownWithOneSession() {
482+ LightDM.Sessions.testScenario = "singleSession"
483+ compare(LightDM.Sessions.count, 1);
484+
485+ var sessionChooserButton = findChild(view, "sessionChooserButton");
486+ tryCompare(sessionChooserButton, "visible", false);
487+ }
488+
489+ function test_sessionIconShownWithMultipleSessions() {
490+ LightDM.Sessions.testScenario = "multipleSessions"
491+ compare(LightDM.Sessions.count > 1, true);
492+
493+ var sessionChooserButton = findChild(view, "sessionChooserButton");
494+ tryCompare(sessionChooserButton, "visible", true);
495+ }
496+
381497 function test_tease(data) {
382498 view.locked = data.locked;
383499 view.dragHandleLeftMargin = data.offset;