Lomiri
Loading...
Searching...
No Matches
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
17import QtQuick 2.12
18import Lomiri.Components 1.3
19import Lomiri.Components.ListItems 1.3
20import "../Components"
21
22FocusScope {
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 LomiriNumberAnimation {}
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}