~ci-train-bot/unity8/unity8-ubuntu-zesty-2167

« back to all changes in this revision

Viewing changes to Greeter/LoginList.qml

  • Committer: Michał Sawicz
  • Date: 2013-06-05 22:03:08 UTC
  • Revision ID: michal.sawicz@canonical.com-20130605220308-yny8fv3futtr04fg
Inital unity8 commit.

Previous history can be found at https://code.launchpad.net/~unity-team/unity/phablet

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2013 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.0
 
18
import Ubuntu.Components 0.1
 
19
import LightDM 0.1 as LightDM
 
20
 
 
21
Item {
 
22
    id: root
 
23
 
 
24
    property alias userList: userList
 
25
    property alias model: userList.model
 
26
    property alias currentIndex: userList.currentIndex
 
27
 
 
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
 
33
 
 
34
    signal selected(int uid)
 
35
    signal unlocked(int uid)
 
36
 
 
37
    Keys.onEscapePressed: root.resetAuthentication()
 
38
 
 
39
    Rectangle {
 
40
        id: highlightItem
 
41
        anchors {
 
42
            left: parent.left
 
43
            right: parent.right
 
44
            verticalCenter: parent.verticalCenter
 
45
        }
 
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)
 
50
        radius: units.gu(1.5)
 
51
        antialiasing: true
 
52
    }
 
53
 
 
54
    ListView {
 
55
        id: userList
 
56
        objectName: "userList"
 
57
 
 
58
        anchors.fill: parent
 
59
 
 
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
 
65
 
 
66
        readonly property bool movingInternally: moveTimer.running || userList.moving
 
67
 
 
68
        onCountChanged: {
 
69
            // Start authentication when we start up
 
70
            if (!LightDM.Greeter.authenticationUser && userList.currentItem) {
 
71
                root.selected(userList.currentIndex);
 
72
                root.resetAuthentication();
 
73
            }
 
74
        }
 
75
 
 
76
        onCurrentIndexChanged: {
 
77
            if (LightDM.Greeter.authenticationUser != userList.model.data(currentIndex, LightDM.UserRoles.NameRole)) {
 
78
                root.resetAuthentication();
 
79
            }
 
80
        }
 
81
 
 
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);
 
86
            }
 
87
        }
 
88
 
 
89
        delegate: Item {
 
90
            width: parent.width
 
91
            height: root.cellHeight
 
92
 
 
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
 
95
 
 
96
            opacity: {
 
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;
 
103
                if (belowHighlight)
 
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;
 
109
                else
 
110
                    return 1;
 
111
                return 1 - Math.min(1, (Math.abs(highlightDist) + root.cellHeight) / ((root.numAboveBelow + 1) * root.cellHeight))
 
112
            }
 
113
 
 
114
            Label {
 
115
                objectName: "username" + index
 
116
 
 
117
                anchors {
 
118
                    left: parent.left
 
119
                    leftMargin: units.gu(2)
 
120
                    right: parent.right
 
121
                    rightMargin: units.gu(2)
 
122
                    top: parent.top
 
123
                    // Add an offset to topMargin for any items below the highlight
 
124
                    topMargin: units.gu(1) + (parent.belowHighlight ? parent.belowOffset : 0)
 
125
                }
 
126
                text: realName
 
127
                color: "white"
 
128
                elide: Text.ElideRight
 
129
 
 
130
                Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
 
131
            }
 
132
 
 
133
            MouseArea {
 
134
                anchors {
 
135
                    left: parent.left
 
136
                    right: parent.right
 
137
                    top: parent.top
 
138
                    // Add an offset to topMargin for any items below the highlight
 
139
                    topMargin: parent.belowHighlight ? parent.belowOffset : 0
 
140
                }
 
141
                height: parent.height
 
142
                enabled: userList.currentIndex !== index
 
143
                onClicked: {
 
144
                    moveTimer.start();
 
145
                    userList.currentIndex = index;
 
146
                }
 
147
 
 
148
                Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
 
149
            }
 
150
        }
 
151
 
 
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)
 
154
        Timer {
 
155
            id: moveTimer
 
156
            running: false
 
157
            repeat: false
 
158
            interval: root.moveDuration
 
159
        }
 
160
    }
 
161
 
 
162
    Label {
 
163
        id: infoLabel
 
164
        objectName: "infoLabel"
 
165
        anchors {
 
166
            bottom: passwordInput.top
 
167
            left: parent.left
 
168
            topMargin: units.gu(1)
 
169
            bottomMargin: units.gu(1)
 
170
            leftMargin: units.gu(2)
 
171
            rightMargin: units.gu(1)
 
172
        }
 
173
 
 
174
        color: "white"
 
175
        width: root.width - anchors.leftMargin - anchors.rightMargin
 
176
        fontSize: "small"
 
177
        textFormat: Text.StyledText
 
178
        clip: true
 
179
 
 
180
        opacity: (userList.movingInternally || text == "") ? 0 : 1
 
181
        Behavior on opacity {
 
182
            NumberAnimation { duration: 100 }
 
183
        }
 
184
    }
 
185
 
 
186
    TextField {
 
187
        id: passwordInput
 
188
        objectName: "passwordInput"
 
189
        anchors {
 
190
            bottom: highlightItem.bottom
 
191
            horizontalCenter: parent.horizontalCenter
 
192
            margins: units.gu(1)
 
193
        }
 
194
        height: units.gu(4.5)
 
195
        width: parent.width - anchors.margins * 2
 
196
        opacity: userList.movingInternally ? 0 : 1
 
197
 
 
198
        Behavior on opacity {
 
199
            NumberAnimation { duration: 100 }
 
200
        }
 
201
 
 
202
        onAccepted: {
 
203
            if (!enabled)
 
204
                return;
 
205
            root.focus = true; // so that it can handle Escape presses for us
 
206
            enabled = false;
 
207
            LightDM.Greeter.respond(text);
 
208
        }
 
209
        Keys.onEscapePressed: root.resetAuthentication()
 
210
 
 
211
        Image {
 
212
            anchors {
 
213
                right: parent.right
 
214
                rightMargin: units.gu(2)
 
215
                verticalCenter: parent.verticalCenter
 
216
            }
 
217
            visible: LightDM.Greeter.promptless
 
218
            source: "graphics/icon_arrow.png"
 
219
        }
 
220
 
 
221
        SequentialAnimation {
 
222
            id: wrongPasswordAnimation
 
223
            NumberAnimation {
 
224
                target: passwordInput
 
225
                property: "anchors.horizontalCenterOffset"
 
226
                duration: 50
 
227
                easing.type: Easing.InQuad
 
228
                to: units.gu(2)
 
229
            }
 
230
            NumberAnimation {
 
231
                target: passwordInput
 
232
                property: "anchors.horizontalCenterOffset"
 
233
                duration: 100
 
234
                easing.type: Easing.InOutQuad
 
235
                to: -units.gu(2)
 
236
            }
 
237
            NumberAnimation {
 
238
                target: passwordInput
 
239
                easing.type: Easing.OutElastic
 
240
                properties: "anchors.horizontalCenterOffset"
 
241
                to: 0
 
242
                duration: 400
 
243
                easing.overshoot: units.gu(2)
 
244
            }
 
245
        }
 
246
 
 
247
        Connections {
 
248
            target: Qt.inputMethod
 
249
            onVisibleChanged: {
 
250
                if (!Qt.inputMethod.visible) {
 
251
                    passwordInput.focus = false;
 
252
                }
 
253
            }
 
254
        }
 
255
 
 
256
    }
 
257
 
 
258
    MouseArea {
 
259
        anchors.fill: passwordInput
 
260
        enabled: LightDM.Greeter.promptless
 
261
        onClicked: {
 
262
            if (LightDM.Greeter.authenticated)
 
263
                root.unlocked(userList.currentIndex);
 
264
            else
 
265
                root.resetAuthentication();
 
266
        }
 
267
    }
 
268
 
 
269
    function resetAuthentication() {
 
270
        if (!userList.currentItem) {
 
271
            return;
 
272
        }
 
273
        infoLabel.text = "";
 
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));
 
280
    }
 
281
 
 
282
    Connections {
 
283
        target: LightDM.Greeter
 
284
 
 
285
        onShowMessage: {
 
286
            // inefficient, but we only rarely deal with messages
 
287
            text = text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\n/g, "<br>");
 
288
            if (isError)
 
289
                text = "<font color=\"#df382c\">" + text + "</font>"
 
290
            if (infoLabel.text == "")
 
291
                infoLabel.text = text;
 
292
            else
 
293
                infoLabel.text = infoLabel.text + "<br>" + text;
 
294
        }
 
295
 
 
296
        onShowPrompt: {
 
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;
 
304
        }
 
305
 
 
306
        onAuthenticationComplete: {
 
307
            if (LightDM.Greeter.promptless) {
 
308
                passwordInput.placeholderText = LightDM.Greeter.authenticated ? "Tap to unlock" : "Retry";
 
309
                return;
 
310
            }
 
311
            if (LightDM.Greeter.authenticated) {
 
312
                root.unlocked(userList.currentIndex);
 
313
            } else {
 
314
                wrongPasswordAnimation.start();
 
315
                root.resetAuthentication();
 
316
                passwordInput.focus = true;
 
317
            }
 
318
            passwordInput.text = "";
 
319
        }
 
320
    }
 
321
}