Lomiri
Loading...
Searching...
No Matches
TextPrompt.qml
1/*
2 * Copyright (C) 2021 Capsia
3 * Copyright (C) 2016 Canonical, Ltd.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18import QtQuick 2.15
19import Lomiri.Components 1.3
20import "../Components"
21
22FocusScope {
23 id: root
24 objectName: "promptPassword"
25
26 property string text
27 property bool isSecret
28 property bool interactive: true
29 property bool loginError: false
30 property bool hasKeyboard: false
31 property alias enteredText: passwordInput.text
32
33 signal clicked()
34 signal canceled()
35 signal accepted(string response)
36
37 StyledItem {
38 id: d
39
40 readonly property color textColor: passwordInput.enabled ? theme.palette.normal.raisedText
41 : theme.palette.disabled.raisedText
42 readonly property color selectedColor: passwordInput.enabled ? theme.palette.normal.raised
43 : theme.palette.disabled.raised
44 readonly property color drawColor: passwordInput.enabled ? theme.palette.normal.raisedSecondaryText
45 : theme.palette.disabled.raisedSecondaryText
46 readonly property color errorColor: passwordInput.enabled ? theme.palette.normal.negative
47 : theme.palette.disabled.negative
48 }
49
50 Rectangle {
51 anchors.fill: parent
52 radius: units.gu(0.5)
53 color: "#7A111111"
54 Behavior on border.color {
55 ColorAnimation{}
56 }
57 border {
58 color: root.loginError ? d.errorColor : d.drawColor
59 width: root.loginError ? units.dp(2): units.dp(1)
60 }
61 }
62
63 TextField {
64 id: passwordInput
65 objectName: "promptField"
66 anchors.fill: parent
67 focus: root.focus
68
69 opacity: fakeLabel.visible ? 0 : 1
70 activeFocusOnTab: true
71 onActiveFocusChanged: if (activeFocus) Qt.inputMethod.show()
72
73 onSelectedTextChanged: passwordInput.deselect()
74
75 validator: RegExpValidator {
76 regExp: /^.*$/
77 }
78
79 inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText |
80 Qt.ImhMultiLine // so OSK doesn't close on Enter
81 echoMode: root.isSecret ? TextInput.Password : TextInput.Normal
82 hasClearButton: false
83
84 passwordCharacter: "●"
85 color: d.drawColor
86
87 readonly property real frameSpacing: units.gu(1)
88
89 style: StyledItem {
90 anchors.fill: parent
91 styleName: "FocusShape"
92
93 // Properties needed by TextField
94 readonly property color color: d.textColor
95 readonly property color selectedTextColor: d.selectedColor
96 readonly property color selectionColor: d.textColor
97 readonly property color borderColor: "transparent"
98 readonly property color backgroundColor: "transparent"
99 readonly property color errorColor: d.errorColor
100 readonly property real frameSpacing: styledItem.frameSpacing
101
102 // Properties needed by FocusShape
103 readonly property bool enabled: styledItem.enabled
104 readonly property bool keyNavigationFocus: styledItem.keyNavigationFocus
105 property bool activeFocusOnTab
106 }
107
108 secondaryItem: [
109 Row {
110 id: extraIcons
111 spacing: passwordInput.frameSpacing
112 anchors.verticalCenter: parent.verticalCenter
113 Icon {
114 name: "keyboard-caps-enabled"
115 height: units.gu(3)
116 width: units.gu(3)
117 color: d.drawColor
118 visible: root.isSecret && false // TODO: detect when caps lock is on
119 anchors.verticalCenter: parent.verticalCenter
120 }
121 Icon {
122 objectName: "greeterPromptKeyboardButton"
123 name: "input-keyboard-symbolic"
124 height: units.gu(3)
125 width: units.gu(3)
126 color: d.drawColor
127 visible: !lomiriSettings.alwaysShowOsk && root.hasKeyboard
128 anchors.verticalCenter: parent.verticalCenter
129 MouseArea {
130 anchors.fill: parent
131 onClicked: lomiriSettings.alwaysShowOsk = true
132 }
133 }
134 Icon {
135 name: "dialog-warning-symbolic"
136 height: units.gu(3)
137 width: units.gu(3)
138 color: d.drawColor
139 visible: root.loginError
140 anchors.verticalCenter: parent.verticalCenter
141 }
142 Icon {
143 name: "toolkit_chevron-ltr_2gu"
144 height: units.gu(2.5)
145 width: units.gu(2.5)
146 color: d.drawColor
147 visible: !root.loginError
148 anchors.verticalCenter: parent.verticalCenter
149 MouseArea {
150 anchors.fill: parent
151 onClicked: root.accepted(passwordInput.text)
152 }
153 }
154 }
155 ]
156
157 onDisplayTextChanged: {
158 // We use onDisplayTextChanged instead of onTextChanged because
159 // displayText changes after text and if we did this before it
160 // updated, we would use the wrong displayText for fakeLabel.
161 root.loginError = false;
162 }
163
164 onAccepted: respond()
165
166 function respond() {
167 if (root.interactive) {
168 root.accepted(passwordInput.text);
169 }
170 }
171
172 Keys.onEscapePressed: {
173 lomiriSettings.alwaysShowOsk = false
174 root.canceled();
175 event.accepted = true;
176 }
177 }
178
179 // We use our own custom placeholder label instead of the standard
180 // TextField one because the standard one hardcodes baseText as the
181 // palette color, whereas we want raisedSecondaryText.
182 Label {
183 id: passwordHint
184 objectName: "promptHint"
185 anchors {
186 left: passwordInput ? passwordInput.left : undefined
187 right: passwordInput ? passwordInput.right : undefined
188 verticalCenter: passwordInput ? passwordInput.verticalCenter : undefined
189 leftMargin: units.gu(2)
190 rightMargin: anchors.leftMargin + extraIcons.width
191 }
192 text: root.text
193 visible: passwordInput.text == "" && !passwordInput.inputMethodComposing
194 enabled: visible
195 color: d.drawColor
196 elide: Text.ElideRight
197 }
198
199 // Have a fake label that covers the text field after the user presses
200 // enter. What we *really* want is a disabled mode that doesn't lose OSK
201 // focus. Because our goal here is simply to keep the OSK up while
202 // we wait for PAM to get back to us, and while waiting, we don't want
203 // the user to be able to edit the field (simply because it would look
204 // weird if we allowed that). But until we have such a disabled mode,
205 // we'll fake it by covering the real text field with a label.
206 FadingLabel {
207 id: fakeLabel
208 anchors.verticalCenter: parent ? parent.verticalCenter : undefined
209 anchors.left: parent ? parent.left : undefined
210 anchors.right: parent ? parent.right : undefined
211 anchors.leftMargin: passwordInput.frameSpacing * 2
212 anchors.rightMargin: passwordInput.frameSpacing * 2 + extraIcons.width
213 color: d.drawColor
214 text: passwordInput.displayText
215 visible: !root.interactive
216 enabled: visible
217 }
218}