Unity 8
GreeterPrompt.qml
1 /*
2  * Copyright (C) 2016 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.4
18 import Ubuntu.Components 1.3
19 import "../Components"
20 
21 FocusScope {
22  id: root
23  implicitHeight: units.gu(5)
24  focus: true
25 
26  property bool isPrompt
27  property bool isAlphanumeric
28  property string text
29  property bool isSecret
30 
31  signal clicked()
32  signal canceled()
33  signal responded(string text)
34 
35  function reset() {
36  passwordInput.text = "";
37  fakeLabel.text = "";
38  d.enabled = true;
39  }
40 
41  function showFakePassword() {
42  // Just a silly hack for looking like 4 pin numbers got entered, if
43  // a fingerprint was used and we happen to be using a pin. This was
44  // a request from Design.
45  if (isSecret && isPrompt && !isAlphanumeric) {
46  d.enabled = false;
47  text = "...."; // actual text doesn't matter
48  }
49  }
50 
51  StyledItem {
52  id: d
53 
54  property bool enabled: true
55  readonly property color textColor: passwordInput.enabled ? theme.palette.normal.raisedText
56  : theme.palette.disabled.raisedText
57  readonly property color selectedColor: passwordInput.enabled ? theme.palette.normal.raised
58  : theme.palette.disabled.raised
59  readonly property color drawColor: passwordInput.enabled ? theme.palette.normal.raisedSecondaryText
60  : theme.palette.disabled.raisedSecondaryText
61  readonly property color errorColor: passwordInput.enabled ? theme.palette.normal.negative
62  : theme.palette.disabled.negative
63 
64  onEnabledChanged: {
65  if (!enabled) {
66  fakeLabel.text = passwordInput.displayText;
67  }
68  }
69  }
70 
71  Rectangle {
72  anchors.fill: parent
73  border.width: units.dp(1)
74  border.color: d.drawColor
75  radius: units.gu(0.5)
76  color: "transparent"
77  }
78 
79  Component.onCompleted: updateFocus()
80  onIsPromptChanged: updateFocus()
81  function updateFocus() {
82  if (root.isPrompt) {
83  passwordInput.focus = true;
84  } else {
85  promptButton.focus = true;
86  }
87  }
88 
89  Rectangle {
90  id: promptButton
91  objectName: "promptButton"
92  anchors.fill: parent
93  visible: !root.isPrompt
94 
95  function triggered() {
96  if (d.enabled) {
97  d.enabled = false;
98  root.clicked();
99  }
100  }
101 
102  Rectangle {
103  height: parent.height;
104  width: parent.width
105  color: "transparent"
106  border {
107  color: d.textColor
108  width: units.dp(1)
109  }
110  }
111 
112  Keys.onReturnPressed: triggered();
113  Keys.onEnterPressed: triggered();
114  MouseArea {
115  anchors.fill: parent
116  onClicked: parent.triggered();
117  }
118 
119  Label {
120  anchors.centerIn: parent
121  color: d.textColor
122  text: root.text
123  }
124  }
125 
126  TextField {
127  id: passwordInput
128  objectName: "promptField"
129  anchors.fill: parent
130  visible: root.isPrompt
131  opacity: fakeLabel.visible ? 0 : 1
132 
133  validator: RegExpValidator {
134  regExp: root.isAlphanumeric ? /^.*$/ : /^\d{4}$/
135  }
136 
137  inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText |
138  Qt.ImhMultiLine | // so OSK doesn't close on Enter
139  (root.isAlphanumeric ? Qt.ImhNone : Qt.ImhDigitsOnly)
140  echoMode: root.isSecret ? TextInput.Password : TextInput.Normal
141  hasClearButton: false
142 
143  readonly property real frameSpacing: units.gu(0.5)
144 
145  style: Item {
146  property color color: d.textColor
147  property color selectedTextColor: d.selectedColor
148  property color selectionColor: d.textColor
149  property color borderColor: "transparent"
150  property color backgroundColor: "transparent"
151  property color errorColor: d.errorColor
152  property real frameSpacing: passwordInput.frameSpacing
153  anchors.fill: parent
154  }
155 
156  secondaryItem: [
157  Icon {
158  id: capsIcon
159  name: "keyboard-caps-enabled"
160  height: units.gu(3)
161  width: units.gu(3)
162  color: d.textColor
163  visible: root.isSecret && false // TODO: detect when caps lock is on
164  readonly property real visibleWidth: visible ? width + passwordInput.frameSpacing : 0
165  }
166  ]
167 
168  onDisplayTextChanged: {
169  // We use onDisplayTextChanged instead of onTextChanged because
170  // displayText changes after text and if we did this before it
171  // updated, we would use the wrong displayText for fakeLabel.
172  if (!isAlphanumeric && text.length >= 4) {
173  // hard limit of 4 for passcodes right now
174  respond();
175  }
176  }
177 
178  onAccepted: respond()
179 
180  function respond() {
181  if (d.enabled) {
182  d.enabled = false;
183  root.responded(text);
184  }
185  }
186 
187  Keys.onEscapePressed: {
188  root.canceled();
189  event.accepted = true;
190  }
191 
192  // We use our own custom placeholder label instead of the standard
193  // TextField one because the standard one hardcodes baseText as the
194  // palette color, whereas we want raisedSecondaryText.
195  Label {
196  id: hint
197  anchors {
198  left: parent.left
199  right: parent.right
200  verticalCenter: parent.verticalCenter
201  leftMargin: units.gu(1.5)
202  rightMargin: anchors.leftMargin + capsIcon.visibleWidth
203  }
204  text: root.text
205  visible: passwordInput.text == "" && !passwordInput.inputMethodComposing
206  color: d.drawColor
207  elide: Text.ElideRight
208  }
209  }
210 
211  // Have a fake label that covers the text field after the user presses
212  // enter. What we *really* want is a disabled mode that doesn't lose OSK
213  // focus. Because our goal here is simply to keep the OSK up while
214  // we wait for PAM to get back to us, and while waiting, we don't want
215  // the user to be able to edit the field (simply because it would look
216  // weird if we allowed that). But until we have such a disabled mode,
217  // we'll fake it by covering the real text field with a label.
218  FadingLabel {
219  id: fakeLabel
220  anchors.verticalCenter: parent.verticalCenter
221  anchors.left: parent.left
222  anchors.right: parent.right
223  anchors.leftMargin: passwordInput.frameSpacing * 2
224  anchors.rightMargin: passwordInput.frameSpacing * 2 + capsIcon.visibleWidth
225  color: d.drawColor
226  visible: root.isPrompt && !d.enabled
227  }
228 }