Unity 8
PinPrompt.qml
1 import QtQuick 2.12
2 import Ubuntu.Components 1.3
3 import "../Components"
4 
5 FocusScope {
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: !unity8Settings.alwaysShowOsk && root.hasKeyboard
146  anchors.verticalCenter: parent.verticalCenter
147  MouseArea {
148  anchors.fill: parent
149  onClicked: unity8Settings.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  Label {
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 }