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"
26 property alias model: userList.model
27 property bool alphanumeric: true
28 property int currentIndex
32 readonly property int numAboveBelow: 4
33 readonly property int cellHeight: units.gu(5)
34 readonly property int highlightedHeight: units.gu(15)
35 readonly property int moveDuration: 200
36 readonly property string currentUser: userList.currentItem.username
37 property bool wasPrompted: false
39 signal selected(int index)
40 signal responded(string response)
42 function tryToUnlock() {
44 passwordInput.forceActiveFocus();
47 root.selected(currentIndex);
54 function showMessage(html) {
55 if (infoLabel.text === "") {
56 infoLabel.text = html;
58 infoLabel.text += "<br>" + html;
62 function showPrompt(text, isSecret, isDefaultPrompt) {
63 passwordInput.text = isDefaultPrompt ? alphanumeric ? i18n.tr("Passphrase")
66 passwordInput.isPrompt = true;
67 passwordInput.isSecret = isSecret;
68 passwordInput.reset();
72 function showError() {
73 wrongPasswordAnimation.start();
74 root.resetAuthentication();
78 root.resetAuthentication();
84 function checkIfPromptless() {
85 if (!waiting && !wasPrompted) {
86 passwordInput.isPrompt = false;
87 passwordInput.text = root.locked ? i18n.tr("Retry")
93 onWaitingChanged: d.checkIfPromptless()
94 onLockedChanged: d.checkIfPromptless()
96 theme: ThemeSettings {
97 name: "Ubuntu.Components.Themes.Ambiance"
101 selected(currentIndex - 1);
102 event.accepted = true;
104 Keys.onDownPressed: {
105 selected(currentIndex + 1);
106 event.accepted = true;
108 Keys.onEscapePressed: {
109 selected(currentIndex);
110 event.accepted = true;
113 onCurrentIndexChanged: {
114 userList.currentIndex = currentIndex;
120 topMargin: -units.gu(1)
121 leftMargin: -units.gu(1.5)
122 rightMargin: -units.gu(1.5)
123 bottomMargin: -units.gu(1.5)
125 source: "../Stages/graphics/dropshadow2gu.sci"
133 leftMargin: units.gu(2)
135 rightMargin: units.gu(2)
136 verticalCenter: parent.verticalCenter
138 height: root.highlightedHeight
139 aspect: UbuntuShape.Flat
140 backgroundColor: theme.palette.normal.raised
145 objectName: "userList"
148 anchors.leftMargin: units.gu(2)
149 anchors.rightMargin: units.gu(2)
151 preferredHighlightBegin: userList.height / 2 - root.highlightedHeight / 2
152 preferredHighlightEnd: userList.height / 2 - root.highlightedHeight / 2
153 highlightRangeMode: ListView.StrictlyEnforceRange
154 highlightMoveDuration: root.moveDuration
155 flickDeceleration: 10000
156 interactive: count > 1
158 readonly property bool movingInternally: moveTimer.running || userList.moving
159 onMovingInternallyChanged: {
160 if (!movingInternally) {
161 root.selected(currentIndex);
165 onCurrentIndexChanged: {
166 root.resetAuthentication();
172 height: root.cellHeight
174 readonly property bool belowHighlight: (userList.currentIndex < 0 && index > 0) || (userList.currentIndex >= 0 && index > userList.currentIndex)
175 readonly property int belowOffset: root.highlightedHeight - root.cellHeight
176 readonly property string username: name
179 // The goal here is to make names less and less opaque as they
180 // leave the highlight area. Can't simply use index, because
181 // that can change quickly if the user clicks at edges of
182 // list. So we use actual pixel distance.
183 var highlightDist = 0;
184 var realY = y - userList.contentY;
186 realY += belowOffset;
187 if (realY + height <= highlightItem.y)
188 highlightDist = realY + height - highlightItem.y;
189 else if (realY >= highlightItem.y + root.highlightedHeight)
190 highlightDist = realY - highlightItem.y - root.highlightedHeight;
193 return 1 - Math.min(1, (Math.abs(highlightDist) + root.cellHeight) / ((root.numAboveBelow + 1) * root.cellHeight))
197 objectName: "username" + index
201 leftMargin: units.gu(2)
203 rightMargin: units.gu(2)
205 // Add an offset to bottomMargin for any items below the highlight
206 bottomMargin: -(units.gu(4) + (parent.belowHighlight ? parent.belowOffset : 0))
209 color: userList.currentIndex !== index ? theme.palette.normal.raised : theme.palette.normal.raisedText
211 Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
219 // Add an offset to topMargin for any items below the highlight
220 topMargin: parent.belowHighlight ? parent.belowOffset : 0
222 height: parent.height
223 enabled: userList.currentIndex !== index && parent.opacity > 0
224 onClicked: root.selected(index)
226 Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
230 // This is needed because ListView.moving is not true if the ListView
231 // moves because of an internal event (e.g. currentIndex has changed)
236 interval: root.moveDuration
242 objectName: "infoLabel"
244 bottom: passwordInput.top
245 left: highlightItem.left
246 topMargin: units.gu(1)
247 bottomMargin: units.gu(1)
248 leftMargin: units.gu(2)
249 rightMargin: units.gu(1)
252 color: theme.palette.normal.raisedText
253 width: root.width - anchors.leftMargin - anchors.rightMargin
255 textFormat: Text.StyledText
257 opacity: (userList.movingInternally || text == "") ? 0 : 1
258 Behavior on opacity {
259 NumberAnimation { duration: 100 }
265 objectName: "passwordInput"
267 bottom: highlightItem.bottom
268 horizontalCenter: highlightItem.horizontalCenter
271 width: highlightItem.width - anchors.margins * 2
272 opacity: userList.movingInternally ? 0 : 1
274 isAlphanumeric: root.alphanumeric
276 onClicked: root.tryToUnlock()
277 onResponded: root.responded(text)
278 onCanceled: root.selected(currentIndex)
280 Behavior on opacity {
281 NumberAnimation { duration: 100 }
284 WrongPasswordAnimation {
285 id: wrongPasswordAnimation
286 target: passwordInput
290 function resetAuthentication() {
291 if (!userList.currentItem) {
295 passwordInput.reset();
296 root.wasPrompted = false;