Lomiri
PinPrompt.qml
1import QtQuick 2.12
2import Lomiri.Components 1.3
3import "../Components"
4
5FocusScope {
6 id: root
7
8 property string text
9 property bool isSecret
10 property bool interactive: true
11 property bool loginError: false
12 property bool hasKeyboard: false
13 property alias enteredText: passwordInput.text
14
15 signal clicked()
16 signal canceled()
17 signal accepted(string response)
18
19 StyledItem {
20 id: d
21
22 readonly property color textColor: passwordInput.enabled ? theme.palette.normal.raisedText
23 : theme.palette.disabled.raisedText
24 readonly property color selectedColor: passwordInput.enabled ? theme.palette.normal.raised
25 : theme.palette.disabled.raised
26 readonly property color drawColor: passwordInput.enabled ? theme.palette.normal.raisedSecondaryText
27 : theme.palette.disabled.raisedSecondaryText
28 readonly property color errorColor: passwordInput.enabled ? theme.palette.normal.negative
29 : theme.palette.disabled.negative
30 }
31
32 TextField {
33 id: passwordInput
34 objectName: "promptField"
35 anchors.left: extraIcons.left
36 focus: root.focus
37
38 opacity: fakeLabel.visible ? 0 : 1
39 activeFocusOnTab: true
40
41 onSelectedTextChanged: passwordInput.deselect()
42 onCursorPositionChanged: cursorPosition = length
43
44 validator: RegExpValidator {
45 regExp: /^\d{4}$/
46 }
47
48 inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText |
49 Qt.ImhMultiLine | // so OSK doesn't close on Enter
50 Qt.ImhDigitsOnly
51 echoMode: TextInput.Password
52 hasClearButton: false
53
54 cursorDelegate: Item {}
55
56 passwordCharacter: "●"
57 color: d.drawColor
58
59 readonly property real letterSpacing: units.gu(1.75)
60 readonly property real frameSpacing: letterSpacing
61
62 font.pixelSize: units.gu(3)
63 font.letterSpacing: letterSpacing
64
65 style: StyledItem {
66 anchors.fill: parent
67 styleName: "FocusShape"
68
69 // Properties needed by TextField
70 readonly property color color: d.textColor
71 readonly property color selectedTextColor: d.selectedColor
72 readonly property color selectionColor: d.textColor
73 readonly property color borderColor: "transparent"
74 readonly property color backgroundColor: "transparent"
75 readonly property color errorColor: d.errorColor
76 readonly property real frameSpacing: 0
77
78 // Properties needed by FocusShape
79 readonly property bool enabled: styledItem.enabled
80 readonly property bool keyNavigationFocus: styledItem.keyNavigationFocus
81 property bool activeFocusOnTab
82 }
83
84 onDisplayTextChanged: {
85 // We use onDisplayTextChanged instead of onTextChanged because
86 // displayText changes after text and if we did this before it
87 // updated, we would use the wrong displayText for fakeLabel.
88 root.loginError = false;
89 if (text.length >= 4) {
90 // hard limit of 4 for passcodes right now
91 respond();
92 }
93 }
94
95 onAccepted: respond()
96
97 function respond() {
98 if (root.interactive) {
99 root.accepted(passwordInput.text);
100 }
101 }
102
103 Keys.onEscapePressed: {
104 root.canceled();
105 event.accepted = true;
106 }
107 }
108
109 Row {
110 id: extraIcons
111 spacing: passwordInput.frameSpacing
112 anchors {
113 horizontalCenter: parent ? parent.horizontalCenter : undefined
114 horizontalCenterOffset: passwordInput.letterSpacing / 2
115 verticalCenter: passwordInput ? passwordInput.verticalCenter : undefined
116 }
117
118 Label {
119 id: pinHint
120 objectName: "promptPinHint"
121
122 text: "○○○○"
123 enabled: visible
124 color: d.drawColor
125 font {
126 pixelSize: units.gu(3)
127 letterSpacing: units.gu(1.75)
128 }
129 elide: Text.ElideRight
130 }
131 Icon {
132 name: "keyboard-caps-enabled"
133 height: units.gu(3)
134 width: height
135 color: d.drawColor
136 visible: false // TODO: detect when caps lock is on
137 anchors.verticalCenter: parent.verticalCenter
138 }
139 Icon {
140 objectName: "greeterPromptKeyboardButton"
141 name: "input-keyboard-symbolic"
142 height: units.gu(3)
143 width: height
144 color: d.drawColor
145 visible: !lomiriSettings.alwaysShowOsk && root.hasKeyboard
146 anchors.verticalCenter: parent.verticalCenter
147 MouseArea {
148 anchors.fill: parent
149 onClicked: lomiriSettings.alwaysShowOsk = true
150 }
151 }
152 Icon {
153 name: "dialog-warning-symbolic"
154 height: units.gu(3)
155 width: height
156 color: d.drawColor
157 visible: root.loginError
158 anchors.verticalCenter: parent.verticalCenter
159 }
160 }
161
162 // Have a fake label that covers the text field after the user presses
163 // enter. What we *really* want is a disabled mode that doesn't lose OSK
164 // focus. Because our goal here is simply to keep the OSK up while
165 // we wait for PAM to get back to us, and while waiting, we don't want
166 // the user to be able to edit the field (simply because it would look
167 // weird if we allowed that). But until we have such a disabled mode,
168 // we'll fake it by covering the real text field with a label.
169 FadingLabel {
170 id: fakeLabel
171 anchors.verticalCenter: extraIcons ? extraIcons.verticalCenter : undefined
172 anchors.left: extraIcons ? extraIcons.left : undefined
173 anchors.right: parent ? parent.right : undefined
174 anchors.rightMargin: passwordInput.frameSpacing * 2 + extraIcons.width
175 color: d.drawColor
176 font {
177 pixelSize: pinHint.font.pixelSize
178 letterSpacing: pinHint.font.letterSpacing
179 }
180 text: passwordInput.displayText
181 visible: !root.interactive
182 enabled: visible
183 }
184}