2
* Copyright (C) 2013 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 Ubuntu.Components 0.1
19
import LightDM 0.1 as LightDM
24
property alias userList: userList
25
property alias model: userList.model
26
property alias currentIndex: userList.currentIndex
28
readonly property int numAboveBelow: 4
29
readonly property int cellHeight: units.gu(5)
30
readonly property int highlightedHeight: units.gu(10)
31
readonly property int moveDuration: 200
32
property bool wasPrompted: false
34
signal selected(int uid)
35
signal unlocked(int uid)
37
Keys.onEscapePressed: root.resetAuthentication()
44
verticalCenter: parent.verticalCenter
46
height: root.highlightedHeight
47
color: Qt.rgba(0.1, 0.1, 0.1, 0.4)
48
border.color: Qt.rgba(0.4, 0.4, 0.4, 0.4)
49
border.width: units.dp(1)
56
objectName: "userList"
60
preferredHighlightBegin: userList.height / 2 - root.highlightedHeight / 2
61
preferredHighlightEnd: userList.height / 2 - root.highlightedHeight / 2
62
highlightRangeMode: ListView.StrictlyEnforceRange
63
highlightMoveDuration: root.moveDuration
64
flickDeceleration: 10000
66
readonly property bool movingInternally: moveTimer.running || userList.moving
69
// Start authentication when we start up
70
if (!LightDM.Greeter.authenticationUser && userList.currentItem) {
71
root.selected(userList.currentIndex);
72
root.resetAuthentication();
76
onCurrentIndexChanged: {
77
if (LightDM.Greeter.authenticationUser != userList.model.data(currentIndex, LightDM.UserRoles.NameRole)) {
78
root.resetAuthentication();
82
onMovingInternallyChanged: {
83
// Only emit the selected signal once we stop moving to avoid frequent background changes
84
if (!movingInternally) {
85
root.selected(userList.currentIndex);
91
height: root.cellHeight
93
readonly property bool belowHighlight: (userList.currentIndex < 0 && index > 0) || (userList.currentIndex >= 0 && index > userList.currentIndex)
94
readonly property int belowOffset: root.highlightedHeight - root.cellHeight
97
// The goal here is to make names less and less opaque as they
98
// leave the highlight area. Can't simply use index, because
99
// that can change quickly if the user clicks at edges of
100
// list. So we use actual pixel distance.
101
var highlightDist = 0;
102
var realY = y - userList.contentY;
104
realY += belowOffset;
105
if (realY + height <= highlightItem.y)
106
highlightDist = realY + height - highlightItem.y;
107
else if (realY >= highlightItem.y + root.highlightedHeight)
108
highlightDist = realY - highlightItem.y - root.highlightedHeight;
111
return 1 - Math.min(1, (Math.abs(highlightDist) + root.cellHeight) / ((root.numAboveBelow + 1) * root.cellHeight))
115
objectName: "username" + index
119
leftMargin: units.gu(2)
121
rightMargin: units.gu(2)
123
// Add an offset to topMargin for any items below the highlight
124
topMargin: units.gu(1) + (parent.belowHighlight ? parent.belowOffset : 0)
128
elide: Text.ElideRight
130
Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
138
// Add an offset to topMargin for any items below the highlight
139
topMargin: parent.belowHighlight ? parent.belowOffset : 0
141
height: parent.height
142
enabled: userList.currentIndex !== index
145
userList.currentIndex = index;
148
Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
152
// This is needed because ListView.moving is not true if the ListView
153
// moves because of an internal event (e.g. currentIndex has changed)
158
interval: root.moveDuration
164
objectName: "infoLabel"
166
bottom: passwordInput.top
168
topMargin: units.gu(1)
169
bottomMargin: units.gu(1)
170
leftMargin: units.gu(2)
171
rightMargin: units.gu(1)
175
width: root.width - anchors.leftMargin - anchors.rightMargin
177
textFormat: Text.StyledText
180
opacity: (userList.movingInternally || text == "") ? 0 : 1
181
Behavior on opacity {
182
NumberAnimation { duration: 100 }
188
objectName: "passwordInput"
190
bottom: highlightItem.bottom
191
horizontalCenter: parent.horizontalCenter
194
height: units.gu(4.5)
195
width: parent.width - anchors.margins * 2
196
opacity: userList.movingInternally ? 0 : 1
198
Behavior on opacity {
199
NumberAnimation { duration: 100 }
205
root.focus = true; // so that it can handle Escape presses for us
207
LightDM.Greeter.respond(text);
209
Keys.onEscapePressed: root.resetAuthentication()
214
rightMargin: units.gu(2)
215
verticalCenter: parent.verticalCenter
217
visible: LightDM.Greeter.promptless
218
source: "graphics/icon_arrow.png"
221
SequentialAnimation {
222
id: wrongPasswordAnimation
224
target: passwordInput
225
property: "anchors.horizontalCenterOffset"
227
easing.type: Easing.InQuad
231
target: passwordInput
232
property: "anchors.horizontalCenterOffset"
234
easing.type: Easing.InOutQuad
238
target: passwordInput
239
easing.type: Easing.OutElastic
240
properties: "anchors.horizontalCenterOffset"
243
easing.overshoot: units.gu(2)
248
target: Qt.inputMethod
250
if (!Qt.inputMethod.visible) {
251
passwordInput.focus = false;
259
anchors.fill: passwordInput
260
enabled: LightDM.Greeter.promptless
262
if (LightDM.Greeter.authenticated)
263
root.unlocked(userList.currentIndex);
265
root.resetAuthentication();
269
function resetAuthentication() {
270
if (!userList.currentItem) {
274
passwordInput.placeholderText = "";
275
passwordInput.text = "";
276
passwordInput.focus = false;
277
passwordInput.enabled = true;
278
root.wasPrompted = false;
279
LightDM.Greeter.authenticate(userList.model.data(currentIndex, LightDM.UserRoles.NameRole));
283
target: LightDM.Greeter
286
// inefficient, but we only rarely deal with messages
287
text = text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\n/g, "<br>");
289
text = "<font color=\"#df382c\">" + text + "</font>"
290
if (infoLabel.text == "")
291
infoLabel.text = text;
293
infoLabel.text = infoLabel.text + "<br>" + text;
297
passwordInput.text = "";
298
passwordInput.placeholderText = text;
299
passwordInput.enabled = true;
300
passwordInput.echoMode = isSecret ? TextInput.Password : TextInput.Normal;
301
if (root.wasPrompted) // stay in text field if second prompt
302
passwordInput.focus = true;
303
root.wasPrompted = true;
306
onAuthenticationComplete: {
307
if (LightDM.Greeter.promptless) {
308
passwordInput.placeholderText = LightDM.Greeter.authenticated ? "Tap to unlock" : "Retry";
311
if (LightDM.Greeter.authenticated) {
312
root.unlocked(userList.currentIndex);
314
wrongPasswordAnimation.start();
315
root.resetAuthentication();
316
passwordInput.focus = true;
318
passwordInput.text = "";