Lomiri
TextPrompt.qml
1import QtQuick 2.4
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 Rectangle {
33 anchors.fill: parent
34 radius: units.gu(0.5)
35 color: "#7A111111"
36 Behavior on border.color {
37 ColorAnimation{}
38 }
39 border {
40 color: root.loginError ? d.errorColor : d.drawColor
41 width: root.loginError ? units.dp(2): units.dp(1)
42 }
43 }
44
45 TextField {
46 id: passwordInput
47 objectName: "promptField"
48 anchors.fill: parent
49 focus: root.focus
50
51 opacity: fakeLabel.visible ? 0 : 1
52 activeFocusOnTab: true
53
54 onSelectedTextChanged: passwordInput.deselect()
55
56 validator: RegExpValidator {
57 regExp: /^.*$/
58 }
59
60 inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText |
61 Qt.ImhMultiLine // so OSK doesn't close on Enter
62 echoMode: root.isSecret ? TextInput.Password : TextInput.Normal
63 hasClearButton: false
64
65 passwordCharacter: "●"
66 color: d.drawColor
67
68 readonly property real frameSpacing: units.gu(1)
69
70 style: StyledItem {
71 anchors.fill: parent
72 styleName: "FocusShape"
73
74 // Properties needed by TextField
75 readonly property color color: d.textColor
76 readonly property color selectedTextColor: d.selectedColor
77 readonly property color selectionColor: d.textColor
78 readonly property color borderColor: "transparent"
79 readonly property color backgroundColor: "transparent"
80 readonly property color errorColor: d.errorColor
81 readonly property real frameSpacing: styledItem.frameSpacing
82
83 // Properties needed by FocusShape
84 readonly property bool enabled: styledItem.enabled
85 readonly property bool keyNavigationFocus: styledItem.keyNavigationFocus
86 property bool activeFocusOnTab
87 }
88
89 secondaryItem: [
90 Row {
91 id: extraIcons
92 spacing: passwordInput.frameSpacing
93 anchors.verticalCenter: parent.verticalCenter
94 Icon {
95 name: "keyboard-caps-enabled"
96 height: units.gu(3)
97 width: units.gu(3)
98 color: d.drawColor
99 visible: root.isSecret && false // TODO: detect when caps lock is on
100 anchors.verticalCenter: parent.verticalCenter
101 }
102 Icon {
103 objectName: "greeterPromptKeyboardButton"
104 name: "input-keyboard-symbolic"
105 height: units.gu(3)
106 width: units.gu(3)
107 color: d.drawColor
108 visible: !lomiriSettings.alwaysShowOsk && root.hasKeyboard
109 anchors.verticalCenter: parent.verticalCenter
110 MouseArea {
111 anchors.fill: parent
112 onClicked: lomiriSettings.alwaysShowOsk = true
113 }
114 }
115 Icon {
116 name: "dialog-warning-symbolic"
117 height: units.gu(3)
118 width: units.gu(3)
119 color: d.drawColor
120 visible: root.loginError
121 anchors.verticalCenter: parent.verticalCenter
122 }
123 Icon {
124 name: "toolkit_chevron-ltr_2gu"
125 height: units.gu(2.5)
126 width: units.gu(2.5)
127 color: d.drawColor
128 visible: !root.loginError
129 anchors.verticalCenter: parent.verticalCenter
130 MouseArea {
131 anchors.fill: parent
132 onClicked: root.accepted(passwordInput.text)
133 }
134 }
135 }
136 ]
137
138 onDisplayTextChanged: {
139 // We use onDisplayTextChanged instead of onTextChanged because
140 // displayText changes after text and if we did this before it
141 // updated, we would use the wrong displayText for fakeLabel.
142 root.loginError = false;
143 }
144
145 onAccepted: respond()
146
147 function respond() {
148 if (root.interactive) {
149 root.accepted(passwordInput.text);
150 }
151 }
152
153 Keys.onEscapePressed: {
154 root.canceled();
155 event.accepted = true;
156 }
157 }
158
159 // We use our own custom placeholder label instead of the standard
160 // TextField one because the standard one hardcodes baseText as the
161 // palette color, whereas we want raisedSecondaryText.
162 Label {
163 id: passwordHint
164 objectName: "promptHint"
165 anchors {
166 left: passwordInput ? passwordInput.left : undefined
167 right: passwordInput ? passwordInput.right : undefined
168 verticalCenter: passwordInput ? passwordInput.verticalCenter : undefined
169 leftMargin: units.gu(2)
170 rightMargin: anchors.leftMargin + extraIcons.width
171 }
172 text: root.text
173 visible: passwordInput.text == "" && !passwordInput.inputMethodComposing
174 enabled: visible
175 color: d.drawColor
176 elide: Text.ElideRight
177 }
178
179 // Have a fake label that covers the text field after the user presses
180 // enter. What we *really* want is a disabled mode that doesn't lose OSK
181 // focus. Because our goal here is simply to keep the OSK up while
182 // we wait for PAM to get back to us, and while waiting, we don't want
183 // the user to be able to edit the field (simply because it would look
184 // weird if we allowed that). But until we have such a disabled mode,
185 // we'll fake it by covering the real text field with a label.
186 FadingLabel {
187 id: fakeLabel
188 anchors.verticalCenter: parent ? parent.verticalCenter : undefined
189 anchors.left: parent ? parent.left : undefined
190 anchors.right: parent ? parent.right : undefined
191 anchors.leftMargin: passwordInput.frameSpacing * 2
192 anchors.rightMargin: passwordInput.frameSpacing * 2 + extraIcons.width
193 color: d.drawColor
194 text: passwordInput.displayText
195 visible: !root.interactive
196 enabled: visible
197 }
198}