Lomiri
PromptList.qml
1 /*
2  * Copyright (C) 2017 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.12
18 import Lomiri.Components 1.3
19 import AccountsService 0.1
20 import "../Components"
21 import "." 0.1
22 
23 FocusScope {
24  id: root
25  height: childrenRect.height
26 
27  property bool isLandscape
28  property string usageMode
29  property bool alphanumeric: true
30  property bool interactive: true
31  property bool loginError: false
32  property bool hasKeyboard: false
33  property int maxHeight: units.gu(5)
34  // don't allow custom pincode prompt for multi user in phone context as it will hide the login list
35  readonly property string pinCodeManager: LightDMService.users.count > 1 && root.usageMode === "phone" && root.isLandscape ? AccountsService.defaultPinCodePromptManager : AccountsService.pinCodePromptManager
36 
37  property real defaultPromptWidth: units.gu(20)
38  property real maxPromptHeight: isLandscape ? root.width - units.gu(10) : maxHeight
39 
40  signal responded(string text)
41  signal clicked()
42  signal canceled()
43 
44  function showFakePassword() {
45  for (var i = 0; i < repeater.count; i++) {
46  var item = repeater.itemAt(i).item;
47  if (item.isPrompt) {
48  item.showFakePassword();
49  }
50  }
51  }
52 
53  QtObject {
54  id: d
55 
56  function sendResponse() {
57  for (var i = 0; i < repeater.count; i++) {
58  var item = repeater.itemAt(i).item;
59  if (item.isPrompt) {
60  root.responded(item.enteredText);
61  }
62  }
63  }
64  }
65 
66  Column {
67  width: parent.width
68  spacing: units.gu(0.5)
69 
70  Repeater {
71  id: repeater
72  model: LightDMService.prompts
73 
74  delegate: Loader {
75  id: loader
76 
77  readonly property bool isLabel: model.type == LightDMService.prompts.Message ||
78  model.type == LightDMService.prompts.Error
79  // we want to have properties set at component loading time
80  readonly property var modelText: model.text
81  readonly property var modelType: model.type
82  readonly property var modelIndex: model.index
83 
84  sourceComponent: isLabel ? infoLabel : greeterPrompt
85  anchors.horizontalCenter: parent.horizontalCenter
86 
87  active: root.visible
88 
89  onLoaded: {
90  for (var i = 0; i < repeater.count; i++) {
91  var item = repeater.itemAt(i);
92  if (item && !item.isLabel) {
93  item.focus = true;
94  break;
95  }
96  }
97  loader.item.opacity = 1;
98  }
99  }
100  }
101  }
102 
103  Component {
104  id: infoLabel
105 
106  FadingLabel {
107  objectName: "infoLabel" + modelIndex
108  width: root.defaultPromptWidth
109 
110  readonly property bool isPrompt: false
111 
112  color: modelType === LightDMService.prompts.Message ? theme.palette.normal.raisedSecondaryText
113  : theme.palette.normal.negative
114  fontSize: "small"
115  textFormat: Text.PlainText
116  text: modelText
117 
118  visible: modelType === LightDMService.prompts.Message
119 
120  Behavior on opacity { LomiriNumberAnimation {} }
121  opacity: 0
122  }
123  }
124 
125  Component {
126  id: greeterPrompt
127 
128  GreeterPrompt {
129  objectName: "greeterPrompt" + modelIndex
130  width: isAlternativePinPrompt ? root.width : root.defaultPromptWidth
131  implicitHeight: isAlternativePinPrompt ? root.maxPromptHeight : units.gu(5)
132 
133  property bool isAlphanumeric: modelText !== "" || root.alphanumeric
134  property bool isAlternativePinPrompt: (isPinPrompt && pinCodeManager !== AccountsService.defaultPinCodePromptManager)
135 
136  interactive: root.interactive
137  pinCodeManager: root.pinCodeManager
138  isPrompt: modelType !== LightDMService.prompts.Button
139  isSecret: modelType === LightDMService.prompts.Secret
140  isPinPrompt: isPrompt && !isAlphanumeric && isSecret
141  loginError: root.loginError
142  hasKeyboard: root.hasKeyboard
143  text: modelText ? modelText : (isAlphanumeric ? i18n.tr("Passphrase") : i18n.tr("Passcode"))
144 
145  onClicked: root.clicked()
146  onAccepted: {
147  // If there is another GreeterPrompt, focus it.
148  for (var i = modelIndex + 1; i < repeater.count; i++) {
149  var item = repeater.itemAt(i).item;
150  if (item.isPrompt) {
151  item.forceActiveFocus();
152  return;
153  }
154  }
155 
156  // Nope we're the last one; just send our response.
157  d.sendResponse();
158  }
159 
160  onLoginErrorChanged: {
161  // provide a fallback to password prompt in case of 5 failed attempts
162  if (loginError && isPinPrompt && AccountsService.failedLogins === 5) {
163  isPrompt = true
164  root.alphanumeric = true
165  }
166  }
167  onCanceled: root.canceled()
168 
169  Behavior on opacity { LomiriNumberAnimation {} }
170  opacity: 0
171  }
172  }
173 }