2 * Copyright 2022 UBports Foundation
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/>.
18import Lomiri.Components 1.3
22 objectName: "ClockPinPrompt"
25 property bool isSecret
26 property bool interactive: true
27 property bool loginError: false
28 property bool hasKeyboard: false //unused
29 property string enteredText: ""
31 property int previousNumber: -1
32 property var currentCode: []
33 property int maxnum: 10
34 readonly property int pincodeSize: 4 // hard coded pin code length for now
35 readonly property int minPinCodeDigits: 4
36 readonly property bool validCode: enteredText.length >= minPinCodeDigits
37 property bool isLandscape: width > height
41 signal accepted(string response)
43 onCurrentCodeChanged: {
46 const maxDigits = Math.max(root.minPinCodeDigits, currentCode.length)
47 for( let i = 0; i < maxDigits; i++) {
48 if (i < currentCode.length) {
50 tmpCode += currentCode[i]
56 pinHint.text = tmpText
57 root.enteredText = tmpCode
59 if (root.enteredText.length >= pincodeSize) {
60 root.accepted(root.enteredText);
64 function addNumber (number, fromKeyboard) {
65 let tmpCodes = currentCode
67 currentCode = tmpCodes
68 // don't animate digits while with keyboard
70 repeater.itemAt(number).animation.restart()
72 root.previousNumber = number
75 function removeOne() {
76 let tmpCodes = currentCode
79 currentCode = tmpCodes
90 readonly property color normal: theme.palette.normal.raisedText
91 readonly property color selected: theme.palette.normal.raisedSecondaryText
92 readonly property color selectedCircle: Qt.rgba(selected.r, selected.g, selected.b, 0.2)
93 readonly property color disabled:theme.palette.disabled.raisedSecondaryText
99 anchors.horizontalCenter: parent.horizontalCenter
100 width: Math.max(units.gu(16), contentWidth + units.gu(3))
105 pixelSize: units.gu(3)
106 letterSpacing: units.gu(1.75)
108 secondaryItem: Icon {
110 objectName: "EraseBtn"
113 color: enabled ? d.selected : d.disabled
114 enabled: root.currentCode.length > 0
115 anchors.verticalCenter: parent.verticalCenter
118 onClicked: root.removeOne()
119 onPressAndHold: root.reset()
123 inputMethodHints: Qt.ImhDigitsOnly
125 Keys.onEscapePressed: {
127 event.accepted = true;
131 if(event.key >= Qt.Key_0 && event.key <= Qt.Key_9) {
132 root.addNumber(event.text, true)
133 event.accepted = true;
136 Keys.onReturnPressed: root.accepted(root.enteredText);
137 Keys.onEnterPressed: root.accepted(root.enteredText);
139 Keys.onBackPressed: {
147 objectName: "SelectArea"
149 height: Math.min(parent.height, parent.width)
151 anchors.bottom:parent.bottom
152 // in landscape, let the clock being close to the bottom
153 anchors.bottomMargin: root.isLandscape ? -units.gu(4) : undefined
154 anchors.horizontalCenter: parent.horizontalCenter
161 function reEvaluate() {
162 var child = main.childAt(mouseX, mouseY)
164 if (child !== null && child.number !== undefined) {
165 var number = child.number
166 if (number > -1 && ( root.previousNumber === -1 || number !== root.previousNumber)) {
167 root.addNumber(number)
171 root.previousNumber = -1
176 if (state !== "ENTRY_MODE") {
177 root.state = "ENTRY_MODE"
190 objectName: "CenterCircle"
191 height: main.height / 3
194 property int radiusSquared: radius * radius
195 property alias locker: centerImg.source
196 property alias animation: challengeAnim
197 anchors.centerIn: parent
199 property int number: -1
203 source: "image://theme/lock"
204 anchors.centerIn: parent
207 color: root.validCode ? d.selected : d.disabled
208 onSourceChanged: imgAnim.start()
211 SequentialAnimation {
236 SequentialAnimation {
238 NumberAnimation { target: centerImg; property: "opacity"; from: 0; to: 1; duration: 1000 }
246 objectName: "dotRepeater"
255 property int number: index
256 property alias dot: point
257 property alias animation: anim
259 property int bigR: root.state === "ENTRY_MODE" ? main.height / 3 : 0
260 property int offsetRadius: radius
261 x: (main.width / 2) + bigR * Math.sin(2 * Math.PI * index / root.maxnum) - offsetRadius
262 y: (main.height / 2) - bigR * Math.cos(2 * Math.PI * index / root.maxnum) - offsetRadius
266 font.pixelSize: main.height / 10
267 anchors.centerIn: parent
270 opacity: root.state === "ENTRY_MODE" ? 1 : 0
271 property bool selected: false
273 Behavior on opacity {
274 LomiriNumberAnimation{ duration: 500 }
281 root.addNumber(index)
282 mouse.accepted = false
287 LomiriNumberAnimation { duration: 500 }
290 SequentialAnimation {
300 target: selectionRect
302 to: Qt.rgba(d.selected.r, d.selected.g, d.selected.b, 0.3)
314 target: selectionRect
329 script: root.reset();
333 name: "WRONG_PASSWORD"
334 when: root.loginError
337 locker: "image://theme/dialog-warning-symbolic"
342 transitions: Transition {
343 from: "WRONG_PASSWORD"; to: "ENTRY_MODE";
344 PropertyAction { target: center; property: "locker"; value: "image://theme/dialog-warning-symbolic" }
345 PauseAnimation { duration: 1000 }
348 onActiveFocusChanged: {
349 if (!activeFocus && !pinHint.activeFocus) {
352 root.state = "ENTRY_MODE"
353 pinHint.forceActiveFocus()