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