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"
25 property alias model: userList.model
26 property bool alphanumeric: true
27 property int currentIndex
30 readonly property int numAboveBelow: 4
31 readonly property int cellHeight: units.gu(5)
32 readonly property int highlightedHeight: units.gu(15)
33 readonly property int moveDuration: 200
34 readonly property string currentUser: userList.currentItem.username
35 property bool wasPrompted: false
37 signal selected(int index)
38 signal responded(string response)
40 function tryToUnlock() {
42 passwordInput.forceActiveFocus();
45 root.selected(currentIndex);
52 function showMessage(html) {
53 if (infoLabel.text === "") {
54 infoLabel.text = html;
56 infoLabel.text += "<br>" + html;
60 function showPrompt(text, isSecret, isDefaultPrompt) {
62 passwordInput.reset();
63 passwordInput.isSecret = isSecret;
64 if (wasPrompted) // stay in text field if second prompt
65 passwordInput.focus = true;
69 function showError() {
70 wrongPasswordAnimation.start();
71 root.resetAuthentication();
73 passwordInput.focus = true;
78 root.resetAuthentication();
84 property string promptText
87 theme: ThemeSettings {
88 name: "Ubuntu.Components.Themes.Ambiance"
91 Keys.onEscapePressed: {
92 selected(currentIndex);
95 onCurrentIndexChanged: {
96 userList.currentIndex = currentIndex;
102 topMargin: -units.gu(1)
103 leftMargin: -units.gu(1.5)
104 rightMargin: -units.gu(1.5)
105 bottomMargin: -units.gu(1.5)
107 source: "../Stages/graphics/dropshadow2gu.sci"
115 leftMargin: units.gu(2)
117 rightMargin: units.gu(2)
118 verticalCenter: parent.verticalCenter
120 height: root.highlightedHeight
121 aspect: UbuntuShape.Flat
122 backgroundColor: theme.palette.normal.raised
127 objectName: "userList"
130 anchors.leftMargin: units.gu(2)
131 anchors.rightMargin: units.gu(2)
133 preferredHighlightBegin: userList.height / 2 - root.highlightedHeight / 2
134 preferredHighlightEnd: userList.height / 2 - root.highlightedHeight / 2
135 highlightRangeMode: ListView.StrictlyEnforceRange
136 highlightMoveDuration: root.moveDuration
137 flickDeceleration: 10000
139 readonly property bool movingInternally: moveTimer.running || userList.moving
140 onMovingInternallyChanged: {
141 if (!movingInternally) {
142 root.selected(currentIndex);
146 onCurrentIndexChanged: {
147 root.resetAuthentication();
153 height: root.cellHeight
155 readonly property bool belowHighlight: (userList.currentIndex < 0 && index > 0) || (userList.currentIndex >= 0 && index > userList.currentIndex)
156 readonly property int belowOffset: root.highlightedHeight - root.cellHeight
157 readonly property string username: name
160 // The goal here is to make names less and less opaque as they
161 // leave the highlight area. Can't simply use index, because
162 // that can change quickly if the user clicks at edges of
163 // list. So we use actual pixel distance.
164 var highlightDist = 0;
165 var realY = y - userList.contentY;
167 realY += belowOffset;
168 if (realY + height <= highlightItem.y)
169 highlightDist = realY + height - highlightItem.y;
170 else if (realY >= highlightItem.y + root.highlightedHeight)
171 highlightDist = realY - highlightItem.y - root.highlightedHeight;
174 return 1 - Math.min(1, (Math.abs(highlightDist) + root.cellHeight) / ((root.numAboveBelow + 1) * root.cellHeight))
178 objectName: "username" + index
182 leftMargin: units.gu(2)
184 rightMargin: units.gu(2)
186 // Add an offset to bottomMargin for any items below the highlight
187 bottomMargin: -(units.gu(4) + (parent.belowHighlight ? parent.belowOffset : 0))
190 color: userList.currentIndex !== index ? theme.palette.normal.raised : theme.palette.normal.raisedText
192 Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
200 // Add an offset to topMargin for any items below the highlight
201 topMargin: parent.belowHighlight ? parent.belowOffset : 0
203 height: parent.height
204 enabled: userList.currentIndex !== index && parent.opacity > 0
205 onClicked: root.selected(index)
207 Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
211 // This is needed because ListView.moving is not true if the ListView
212 // moves because of an internal event (e.g. currentIndex has changed)
217 interval: root.moveDuration
223 objectName: "infoLabel"
225 bottom: passwordInput.top
226 left: highlightItem.left
227 topMargin: units.gu(1)
228 bottomMargin: units.gu(1)
229 leftMargin: units.gu(2)
230 rightMargin: units.gu(1)
233 color: theme.palette.normal.raisedText
234 width: root.width - anchors.leftMargin - anchors.rightMargin
236 textFormat: Text.StyledText
238 opacity: (userList.movingInternally || text == "") ? 0 : 1
239 Behavior on opacity {
240 NumberAnimation { duration: 100 }
246 objectName: "passwordInput"
248 bottom: highlightItem.bottom
249 horizontalCenter: highlightItem.horizontalCenter
252 width: highlightItem.width - anchors.margins * 2
253 opacity: userList.movingInternally ? 0 : 1
255 isPrompt: root.wasPrompted
256 isAlphanumeric: root.alphanumeric
258 text: root.wasPrompted ? d.promptText
259 : (root.locked ? i18n.tr("Retry")
262 onClicked: root.tryToUnlock()
263 onResponded: root.responded(text)
264 onCanceled: root.selected(currentIndex)
266 Behavior on opacity {
267 NumberAnimation { duration: 100 }
270 WrongPasswordAnimation {
271 id: wrongPasswordAnimation
272 target: passwordInput
276 function resetAuthentication() {
277 if (!userList.currentItem) {
282 passwordInput.reset();
283 root.wasPrompted = false;