Unity 8
PinLockscreen.qml
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.4
18 import Ubuntu.Components 1.3
19 import Ubuntu.Components.ListItems 1.3
20 import "../Components"
21 
22 FocusScope {
23  id: root
24  focus: true
25 
26  property string infoText
27  property string retryText
28  property string errorText
29  property int minPinLength: -1
30  property int maxPinLength: -1
31  property bool showCancelButton: true
32  property color foregroundColor: "#000000"
33 
34  readonly property string passphrase: pinentryField.text
35 
36  signal entered(string passphrase)
37  signal cancel()
38 
39  property bool entryEnabled: true
40 
41  function clear(showAnimation) {
42  pinentryField.text = "";
43  if (showAnimation) {
44  pinentryField.incorrectOverride = true;
45  wrongPasswordAnimation.start();
46  }
47  }
48 
49  function showText(text) {
50  pinentryField.text = text;
51  }
52 
53  Keys.onPressed: {
54  if (pinentryField.text.length == root.maxPinLength)
55  return;
56 
57  if (event.key === Qt.Key_Backspace) {
58  pinentryField.backspace();
59  } else if (event.key === Qt.Key_Delete || event.key === Qt.Key_Escape) {
60  closeButton.clicked()
61  } else if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
62  confirmButton.clicked()
63  } else {
64  var digit = parseInt(event.text);
65  if (!isNaN(digit) && typeof digit == "number") {
66  pinentryField.appendNumber(digit);
67  }
68  }
69  }
70 
71  Column {
72  anchors {
73  left: parent.left;
74  right: parent.right;
75  verticalCenter: parent.verticalCenter;
76  verticalCenterOffset: Math.max(-units.gu(10), -(root.height - height) / 2) + units.gu(4)
77  }
78  spacing: units.gu(4)
79 
80  Column {
81  id: shakeContainer
82  anchors.horizontalCenter: parent.horizontalCenter
83  width: parent.width
84  spacing: units.gu(1)
85 
86  Label {
87  id: infoField
88  objectName: "infoTextLabel"
89  fontSize: "large"
90  color: root.foregroundColor
91  anchors.horizontalCenter: parent.horizontalCenter
92  text: root.infoText
93  }
94 
95  Item {
96  id: pinContainer
97  anchors { left: parent.left; right: parent.right; margins: units.gu(2) }
98  height: units.gu(4)
99 
100  Row {
101  id: pinentryField
102  objectName: "pinentryField"
103  anchors.horizontalCenter: parent.horizontalCenter
104  anchors.verticalCenter: parent.verticalCenter
105  spacing: Math.max(0, Math.min(units.gu(3), (parent.width / root.maxPinLength) - units.gu(3)))
106 
107  property string text
108  property bool incorrectOverride: false
109 
110  Repeater {
111  model: pinentryField.text.length
112  delegate: Rectangle {
113  color: root.foregroundColor
114  width: Math.min(units.gu(2), (pinContainer.width - pinContainer.height*2 ) / (root.maxPinLength >= 0 ? root.maxPinLength : 16))
115  height: width
116  radius: width / 2
117  }
118  }
119 
120  function appendNumber(number) {
121  if (incorrectOverride) {
122  incorrectOverride = false;
123  }
124 
125  pinentryField.text = pinentryField.text + number
126 
127  if (root.minPinLength > 0 && root.maxPinLength > 0
128  && root.minPinLength == root.maxPinLength && pinentryField.text.length == root.minPinLength) {
129  root.entered(pinentryField.text)
130  }
131  }
132 
133  function backspace() {
134  pinentryField.text = pinentryField.text.substring(0, pinentryField.text.length-1)
135  }
136  }
137  Label {
138  id: wrongNoticeLabel
139  objectName: "wrongNoticeLabel"
140  fontSize: "x-large"
141  color: root.foregroundColor
142  anchors.horizontalCenter: parent.horizontalCenter
143  horizontalAlignment: Text.AlignHCenter
144  text: root.errorText
145  visible: pinentryField.incorrectOverride
146  scale: Math.min(1, parent.width / width)
147  }
148 
149  AbstractButton {
150  objectName: "backspaceIcon"
151  anchors { right: parent.right; top: parent.top; bottom: parent.bottom; margins: -units.gu(1) }
152  width: height
153  enabled: root.entryEnabled
154 
155  Icon {
156  anchors.fill: parent
157  anchors.margins: units.gu(1)
158  name: "erase"
159  color: root.foregroundColor
160  }
161 
162  opacity: (pinentryField.text.length > 0 && !pinentryField.incorrectOverride) ? 1 : 0
163 
164  Behavior on opacity {
165  UbuntuNumberAnimation {}
166  }
167 
168  onClicked: pinentryField.backspace()
169  }
170  }
171 
172  Label {
173  objectName: "retryLabel"
174  fontSize: "x-small"
175  color: root.foregroundColor
176  anchors.horizontalCenter: parent.horizontalCenter
177  text: root.retryText || " "
178  }
179  }
180 
181  Grid {
182  id: numbersGrid
183  objectName: "numbersGrid"
184  anchors { horizontalCenter: parent.horizontalCenter }
185  columns: 3
186 
187  property int maxWidth: Math.min(units.gu(50), root.width - units.gu(8))
188  property int buttonWidth: maxWidth / 3
189  property int buttonHeight: buttonWidth * 2 / 3
190 
191  Repeater {
192  model: 9
193 
194  PinPadButton {
195  objectName: "pinPadButton" + text
196  text: index + 1
197  height: numbersGrid.buttonHeight
198  width: numbersGrid.buttonWidth
199  foregroundColor: root.foregroundColor
200  enabled: root.entryEnabled && (root.maxPinLength == -1 ||
201  pinentryField.text.length < root.maxPinLength ||
202  pinentryField.incorrectOverride)
203 
204  onClicked: {
205  pinentryField.appendNumber(index + 1)
206  }
207  }
208  }
209  Item {
210  height: numbersGrid.buttonHeight
211  width: numbersGrid.buttonWidth
212  }
213  PinPadButton {
214  text: "0"
215  height: numbersGrid.buttonHeight
216  width: numbersGrid.buttonWidth
217  foregroundColor: root.foregroundColor
218  enabled: root.entryEnabled && (root.maxPinLength == -1 ||
219  pinentryField.text.length < root.maxPinLength ||
220  pinentryField.incorrectOverride)
221 
222  onClicked: {
223  pinentryField.appendNumber(0)
224  }
225  }
226  Item {
227  height: numbersGrid.buttonHeight
228  width: numbersGrid.buttonWidth
229  }
230  PinPadButton {
231  id: closeButton
232  iconName: "close"
233  height: units.gu(5) // visual spec has this row a little closer in
234  width: numbersGrid.buttonWidth
235  foregroundColor: root.foregroundColor
236  onClicked: root.cancel()
237  visible: root.showCancelButton
238  }
239  Item {
240  height: units.gu(5)
241  width: numbersGrid.buttonWidth
242  }
243  PinPadButton {
244  id: confirmButton
245  iconName: "tick"
246  objectName: "confirmButton"
247  height: units.gu(5)
248  width: numbersGrid.buttonWidth
249  foregroundColor: root.foregroundColor
250  enabled: root.enabled && pinentryField.text.length >= root.minPinLength
251  visible: root.minPinLength == -1 || root.minPinLength !== root.maxPinLength
252 
253  onClicked: root.entered(pinentryField.text)
254  }
255  }
256  WrongPasswordAnimation {
257  id: wrongPasswordAnimation
258  objectName: "wrongPasswordAnimation"
259  target: shakeContainer
260  }
261  }
262 }