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 alias alphanumeric: promptList.alphanumeric
29 property int currentIndex
32 property alias boxVerticalOffset: highlightItem.y
33 property string _realName
35 readonly property int numAboveBelow: 4
36 readonly property int cellHeight: units.gu(5)
37 readonly property int highlightedHeight: highlightItem.height
38 readonly property int moveDuration: UbuntuAnimation.FastDuration
39 property string currentSession // Initially set by LightDM
40 readonly property string currentUser: userList.currentItem.username
42 signal responded(string response)
43 signal selected(int index)
44 signal sessionChooserButtonClicked()
46 function tryToUnlock() {
47 promptList.forceActiveFocus();
50 function showError() {
51 wrongPasswordAnimation.start();
54 function showFakePassword() {
55 promptList.interactive = false;
56 promptList.showFakePassword();
59 theme: ThemeSettings {
60 name: "Ubuntu.Components.Themes.Ambiance"
64 if (currentIndex > 0) {
65 selected(currentIndex - 1);
67 event.accepted = true;
70 if (currentIndex + 1 < model.count) {
71 selected(currentIndex + 1);
73 event.accepted = true;
75 Keys.onEscapePressed: {
76 selected(currentIndex);
77 event.accepted = true;
80 onCurrentIndexChanged: {
81 userList.currentIndex = currentIndex;
86 objectName: "highlightItem"
89 leftMargin: units.gu(2)
91 rightMargin: units.gu(2)
94 height: Math.max(units.gu(15), promptList.height + units.gu(8))
95 Behavior on height { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
98 // HACK: Work around https://github.com/ubports/unity8/issues/185
99 text: _realName ? _realName : LightDMService.greeter.authenticationUser
100 visible: userList.count == 1
104 topMargin: units.gu(2)
105 leftMargin: units.gu(2)
112 objectName: "userList"
115 anchors.leftMargin: units.gu(2)
116 anchors.rightMargin: units.gu(2)
118 preferredHighlightBegin: highlightItem.y
119 preferredHighlightEnd: highlightItem.y
120 highlightRangeMode: ListView.StrictlyEnforceRange
121 highlightMoveDuration: root.moveDuration
122 interactive: count > 1
124 readonly property bool movingInternally: moveTimer.running || userList.moving
126 onCurrentIndexChanged: {
131 width: userList.width
132 height: root.cellHeight
134 readonly property bool belowHighlight: (userList.currentIndex < 0 && index > 0) || (userList.currentIndex >= 0 && index > userList.currentIndex)
135 readonly property int belowOffset: root.highlightedHeight - root.cellHeight
136 readonly property string userSession: session
137 readonly property string username: name
140 // The goal here is to make names less and less opaque as they
141 // leave the highlight area. Can't simply use index, because
142 // that can change quickly if the user clicks at edges of
143 // list. So we use actual pixel distance.
144 var highlightDist = 0;
145 var realY = y - userList.contentY;
147 realY += belowOffset;
148 if (realY + height <= highlightItem.y)
149 highlightDist = realY + height - highlightItem.y;
150 else if (realY >= highlightItem.y + root.highlightedHeight)
151 highlightDist = realY - highlightItem.y - root.highlightedHeight;
154 return 1 - Math.min(1, (Math.abs(highlightDist) + root.cellHeight) / ((root.numAboveBelow + 1) * root.cellHeight))
158 objectName: "username" + index
159 visible: userList.count != 1 // HACK Hide username label until someone sorts out the anchoring with the keyboard-dismiss animation, Work around https://github.com/ubports/unity8/issues/185
163 leftMargin: units.gu(2)
165 rightMargin: units.gu(2)
167 // Add an offset to bottomMargin for any items below the highlight
168 bottomMargin: -(units.gu(4) + (parent.belowHighlight ? parent.belowOffset : 0))
170 text: userList.currentIndex === index
172 && LightDMService.greeter.authenticationUser !== ""
173 ? LightDMService.greeter.authenticationUser : realName
174 color: userList.currentIndex !== index ? theme.palette.normal.raised
175 : theme.palette.normal.raisedText
177 Component.onCompleted: _realName = realName
179 Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
183 anchors.horizontalCenter: parent.left
184 anchors.horizontalCenterOffset: -units.gu(1)
185 anchors.verticalCenter: parent.verticalCenter
186 color: userList.currentIndex !== index ? theme.palette.normal.raised
187 : theme.palette.normal.focus
188 visible: userList.count > 1 && loggedIn
189 height: units.gu(0.5)
199 // Add an offset to topMargin for any items below the highlight
200 topMargin: parent.belowHighlight ? parent.belowOffset : 0
202 height: parent.height
203 enabled: userList.currentIndex !== index && parent.opacity > 0
204 onClicked: root.selected(index)
206 Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
210 // This is needed because ListView.moving is not true if the ListView
211 // moves because of an internal event (e.g. currentIndex has changed)
216 interval: root.moveDuration
220 // Use an AbstractButton due to icon limitations with Button
223 objectName: "sessionChooserButton"
225 readonly property alias icon: badge.source
227 visible: LightDMService.sessions.count > 1 &&
228 !LightDMService.users.data(userList.currentIndex, LightDMService.userRoles.LoggedInRole)
230 height: units.gu(3.5)
233 activeFocusOnTab: true
235 right: highlightItem.right
236 rightMargin: units.gu(2)
238 top: highlightItem.top
239 topMargin: units.gu(1.5)
246 visible: parent.activeFocus
248 border.color: theme.palette.normal.focus
249 border.width: units.dp(1)
256 anchors.margins: units.dp(3)
257 keyColor: "#ffffff" // icon providers give us white icons
258 color: theme.palette.normal.raisedSecondaryText
259 source: LightDMService.sessions.iconUrl(root.currentSession)
262 Keys.onReturnPressed: {
263 sessionChooserButtonClicked();
264 event.accepted = true;
268 sessionChooserButtonClicked();
271 // Refresh the icon path if looking at different places at runtime
272 // this is mainly for testing
274 target: LightDMService.sessions
275 onIconSearchDirectoriesChanged: {
276 badge.source = LightDMService.sessions.iconUrl(root.currentSession)
283 objectName: "promptList"
285 bottom: highlightItem.bottom
286 horizontalCenter: highlightItem.horizontalCenter
289 width: highlightItem.width - anchors.margins * 2
294 root.selected(currentIndex);
301 root.responded(text);
305 root.selected(currentIndex);
309 target: LightDMService.prompts
310 onModelReset: promptList.interactive = true
314 WrongPasswordAnimation {
315 id: wrongPasswordAnimation
316 objectName: "wrongPasswordAnimation"