2 * Copyright (C) 2013-2016 Canonical, Ltd.
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.
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.
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/>.
18 import QtGraphicalEffects 1.0
19 import Ubuntu.Components 1.3
20 import "../Components"
27 property alias model: userList.model
28 property bool alphanumeric: true
29 property int currentIndex
32 property alias boxVerticalOffset: highlightItem.y
34 readonly property alias passwordInput: passwordInput
35 readonly property int numAboveBelow: 4
36 readonly property int cellHeight: units.gu(5)
37 readonly property int highlightedHeight: units.gu(15)
38 readonly property int moveDuration: UbuntuAnimation.FastDuration
39 property string selectedSession
40 property string currentSession
41 readonly property string currentUser: userList.currentItem.username
42 property bool wasPrompted: false
44 signal loginListSessionChanged(string session)
45 signal responded(string response)
46 signal selected(int index)
47 signal sessionChooserButtonClicked()
49 function tryToUnlock() {
51 passwordInput.forceActiveFocus();
54 root.selected(currentIndex);
61 function showMessage(html) {
62 if (infoLabel.text === "") {
63 infoLabel.text = html;
65 infoLabel.text += "<br>" + html;
69 function showPrompt(text, isSecret, isDefaultPrompt) {
70 passwordInput.text = isDefaultPrompt ? alphanumeric ? i18n.tr("Passphrase")
73 passwordInput.isPrompt = true;
74 passwordInput.isSecret = isSecret;
75 passwordInput.reset();
79 function showError() {
80 wrongPasswordAnimation.start();
81 root.resetAuthentication();
85 root.resetAuthentication();
88 function showFakePassword() {
89 passwordInput.showFakePassword();
95 function checkIfPromptless() {
96 if (!waiting && !wasPrompted) {
97 passwordInput.isPrompt = false;
98 passwordInput.text = root.locked ? i18n.tr("Retry")
104 onWaitingChanged: d.checkIfPromptless()
105 onLockedChanged: d.checkIfPromptless()
107 theme: ThemeSettings {
108 name: "Ubuntu.Components.Themes.Ambiance"
112 selected(currentIndex - 1);
113 event.accepted = true;
115 Keys.onDownPressed: {
116 selected(currentIndex + 1);
117 event.accepted = true;
119 Keys.onEscapePressed: {
120 selected(currentIndex);
121 event.accepted = true;
124 onCurrentIndexChanged: {
125 userList.currentIndex = currentIndex;
130 objectName: "highlightItem"
133 leftMargin: units.gu(2)
135 rightMargin: units.gu(2)
138 height: root.highlightedHeight
143 objectName: "userList"
146 anchors.leftMargin: units.gu(2)
147 anchors.rightMargin: units.gu(2)
149 preferredHighlightBegin: highlightItem.y
150 preferredHighlightEnd: highlightItem.y
151 highlightRangeMode: ListView.StrictlyEnforceRange
152 highlightMoveDuration: root.moveDuration
153 interactive: count > 1
155 readonly property bool movingInternally: moveTimer.running || userList.moving
156 onMovingInternallyChanged: {
157 if (!movingInternally) {
158 root.selected(currentIndex);
162 onCurrentIndexChanged: {
163 root.resetAuthentication();
169 height: root.cellHeight
171 readonly property bool belowHighlight: (userList.currentIndex < 0 && index > 0) || (userList.currentIndex >= 0 && index > userList.currentIndex)
172 readonly property int belowOffset: root.highlightedHeight - root.cellHeight
173 readonly property string userSession: session
174 readonly property string username: name
177 // The goal here is to make names less and less opaque as they
178 // leave the highlight area. Can't simply use index, because
179 // that can change quickly if the user clicks at edges of
180 // list. So we use actual pixel distance.
181 var highlightDist = 0;
182 var realY = y - userList.contentY;
184 realY += belowOffset;
185 if (realY + height <= highlightItem.y)
186 highlightDist = realY + height - highlightItem.y;
187 else if (realY >= highlightItem.y + root.highlightedHeight)
188 highlightDist = realY - highlightItem.y - root.highlightedHeight;
191 return 1 - Math.min(1, (Math.abs(highlightDist) + root.cellHeight) / ((root.numAboveBelow + 1) * root.cellHeight))
195 objectName: "username" + index
199 leftMargin: units.gu(2)
201 rightMargin: units.gu(2)
203 // Add an offset to bottomMargin for any items below the highlight
204 bottomMargin: -(units.gu(4) + (parent.belowHighlight ? parent.belowOffset : 0))
207 color: userList.currentIndex !== index ? theme.palette.normal.raised
208 : theme.palette.normal.raisedText
210 Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
214 anchors.horizontalCenter: parent.left
215 anchors.horizontalCenterOffset: -units.gu(1)
216 anchors.verticalCenter: parent.verticalCenter
217 color: userList.currentIndex !== index ? theme.palette.normal.raised
218 : theme.palette.normal.focus
219 visible: userList.count > 1 && loggedIn
220 height: units.gu(0.5)
230 // Add an offset to topMargin for any items below the highlight
231 topMargin: parent.belowHighlight ? parent.belowOffset : 0
233 height: parent.height
234 enabled: userList.currentIndex !== index && parent.opacity > 0
235 onClicked: root.selected(index)
237 Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
241 // This is needed because ListView.moving is not true if the ListView
242 // moves because of an internal event (e.g. currentIndex has changed)
247 interval: root.moveDuration
251 // Use an AbstractButton due to icon limitations with Button
254 objectName: "sessionChooserButton"
256 readonly property alias icon: badge.source
258 visible: LightDMService.sessions.count > 1 &&
259 !LightDMService.users.data(userList.currentIndex, LightDMService.userRoles.LoggedInRole)
261 height: units.gu(3.5)
264 activeFocusOnTab: true
266 right: highlightItem.right
267 rightMargin: units.gu(2)
269 top: highlightItem.top
270 topMargin: units.gu(1.5)
277 visible: parent.activeFocus
279 border.color: theme.palette.normal.focus
280 border.width: units.dp(1)
287 anchors.margins: units.dp(3)
288 keyColor: "#ffffff" // icon providers give us white icons
289 color: theme.palette.normal.raisedSecondaryText
290 source: LightDMService.sessions.iconUrl(root.currentSession)
293 Keys.onReturnPressed: {
294 sessionChooserButtonClicked();
295 event.accepted = true;
299 sessionChooserButtonClicked();
302 // Refresh the icon path if looking at different places at runtime
303 // this is mainly for testing
305 target: LightDMService.sessions
306 onIconSearchDirectoriesChanged: {
307 badge.source = LightDMService.sessions.iconUrl(root.currentSession)
314 objectName: "infoLabel"
316 bottom: passwordInput.top
317 left: highlightItem.left
318 topMargin: units.gu(1)
319 bottomMargin: units.gu(1)
320 leftMargin: units.gu(2)
321 rightMargin: units.gu(1)
324 color: theme.palette.normal.raisedText
325 width: root.width - anchors.leftMargin - anchors.rightMargin
327 textFormat: Text.StyledText
329 opacity: (userList.movingInternally || text == "") ? 0 : 1
330 Behavior on opacity {
331 NumberAnimation { duration: 100 }
337 objectName: "passwordInput"
339 bottom: highlightItem.bottom
340 horizontalCenter: highlightItem.horizontalCenter
343 width: highlightItem.width - anchors.margins * 2
344 opacity: userList.movingInternally ? 0 : 1
346 activeFocusOnTab: true
347 isAlphanumeric: root.alphanumeric
349 onClicked: root.tryToUnlock()
350 onResponded: root.responded(text)
351 onCanceled: root.selected(currentIndex)
353 Behavior on opacity {
354 NumberAnimation { duration: 100 }
357 WrongPasswordAnimation {
358 id: wrongPasswordAnimation
359 objectName: "wrongPasswordAnimation"
360 target: passwordInput
364 function resetAuthentication() {
365 if (!userList.currentItem) {
369 passwordInput.reset();
370 root.wasPrompted = false;