Lomiri
WideView.qml
1/*
2 * Copyright (C) 2015-2016 Canonical Ltd.
3 * Copyright (C) 2021 UBports Foundation
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.12
19import Lomiri.Components 1.3
20import "." 0.1
21
22FocusScope {
23 id: root
24 objectName: "WideView"
25
26 focus: true
27
28 property alias background: coverPage.background
29 property alias backgroundSourceSize: coverPage.backgroundSourceSize
30 property alias panelHeight: coverPage.panelHeight
31 property alias hasCustomBackground: coverPage.hasCustomBackground
32 property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin
33 property alias infographicModel: coverPage.infographicModel
34 property alias launcherOffset: coverPage.launcherOffset
35 property alias currentIndex: loginList.currentIndex
36 property int delayMinutes // TODO
37 property alias alphanumeric: loginList.alphanumeric
38 property alias hasKeyboard: loginList.hasKeyboard
39 property alias locked: loginList.locked
40 property alias waiting: loginList.waiting
41 property var userModel // Set from outside
42
43 readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
44 readonly property bool fullyShown: coverPage.showProgress === 1
45 readonly property bool required: coverPage.required
46 readonly property alias sessionToStart: loginList.currentSession
47
48 property rect inputMethodRect
49
50 signal selected(int index)
51 signal responded(string response)
52 signal tease()
53 signal emergencyCall() // unused
54
55 function notifyAuthenticationFailed() {
56 loginList.showError();
57 }
58
59 function forceShow() {
60 // Nothing to do, we are always fully shown
61 }
62
63 function tryToUnlock(toTheRight) {
64 if (root.locked) {
65 coverPage.show();
66 loginList.tryToUnlock();
67 return false;
68 } else {
69 var coverChanged = coverPage.shown;
70 if (toTheRight) {
71 coverPage.hideRight();
72 } else {
73 coverPage.hide();
74 }
75 return coverChanged;
76 }
77 }
78
79 function hide() {
80 coverPage.hide();
81 }
82
83 function showFakePassword() {
84 loginList.showFakePassword();
85 }
86
87 Rectangle {
88 anchors.fill: parent
89 color: "black"
90 opacity: coverPage.showProgress * 0.8
91 }
92
93 CoverPage {
94 id: coverPage
95 objectName: "coverPage"
96 height: parent.height
97 width: parent.width
98 draggable: !root.locked && !root.waiting
99 state: "LoginList"
100 blurAreaHeight: loginList.highlightedHeight + units.gu(4.5)
101 blurAreaWidth: loginList.width + units.gu(3)
102 blurAreaX: loginList.x - units.gu(1.5)
103 blurAreaY: loginList.boxVerticalOffset + loginList.y - units.gu(3)
104
105 // Darken background to match CoverPage
106 Rectangle {
107 objectName: "lockscreenShade"
108 anchors.fill: parent
109 color: "black"
110 opacity: root.hasCustomBackground ? 0.1 : 0
111 }
112
113 infographics {
114 anchors.topMargin: parent.height * 0.125
115 anchors.bottomMargin: parent.height * 0.125
116 anchors.leftMargin: loginList.x + loginList.width
117 }
118
119 onTease: root.tease()
120
121 onShowProgressChanged: {
122 if (showProgress === 0 && !root.locked) {
123 root.responded("");
124 }
125 }
126
127 LoginList {
128 id: loginList
129 objectName: "loginList"
130
131 width: units.gu(40)
132 anchors {
133 left: parent.left
134 leftMargin: Math.min(parent.width * 0.16, units.gu(20))
135 top: parent.top
136 bottom: parent.bottom
137 }
138
139 boxVerticalOffset: (height - highlightedHeight -
140 inputMethodRect.height) / 2
141 Behavior on boxVerticalOffset { LomiriNumberAnimation {} }
142
143 model: root.userModel
144 onResponded: root.responded(response)
145 onSelected: root.selected(index)
146 onSessionChooserButtonClicked: parent.state = "SessionsList"
147 onCurrentIndexChanged: setCurrentSession()
148
149 Keys.forwardTo: [sessionChooserLoader.item]
150
151 Component.onCompleted: setCurrentSession()
152
153 function setCurrentSession() {
154 currentSession = LightDMService.users.data(currentIndex, LightDMService.userRoles.SessionRole);
155 }
156 }
157
158 Loader {
159 id: sessionChooserLoader
160
161 height: loginList.height
162 width: loginList.width
163
164 anchors {
165 left: parent.left
166 leftMargin: Math.min(parent.width * 0.16, units.gu(20))
167 top: parent.top
168 }
169
170 active: false
171
172 onLoaded: sessionChooserLoader.item.forceActiveFocus();
173 onActiveChanged: {
174 if (!active) return;
175 item.updateHighlight(loginList.currentSession);
176 }
177
178 Connections {
179 target: sessionChooserLoader.item
180 onSessionSelected: loginList.currentSession = sessionKey
181 onShowLoginList: {
182 coverPage.state = "LoginList"
183 loginList.tryToUnlock();
184 }
185 ignoreUnknownSignals: true
186 }
187 }
188
189 // Use an AbstractButton due to icon limitations with Button
190 AbstractButton {
191 id: sessionChooser
192 objectName: "sessionChooserButton"
193
194 readonly property url icon: LightDMService.sessions.iconUrl(loginList.currentSession)
195
196 visible: LightDMService.sessions.count > 1 &&
197 !LightDMService.users.data(loginList.currentUserIndex, LightDMService.userRoles.LoggedInRole)
198
199 height: units.gu(3.5)
200 width: units.gu(3.5)
201
202 activeFocusOnTab: true
203 anchors {
204 right: parent.right
205 rightMargin: units.gu(2)
206
207 bottom: parent.bottom
208 bottomMargin: units.gu(1.5)
209 }
210
211 Rectangle {
212 id: badgeHighlight
213
214 anchors.fill: parent
215 visible: parent.activeFocus
216 color: "transparent"
217 border.color: theme.palette.normal.focus
218 border.width: units.dp(1)
219 radius: 3
220 }
221
222 Icon {
223 id: badge
224 anchors.fill: parent
225 anchors.margins: units.dp(3)
226 keyColor: "#ffffff" // icon providers give us white icons
227 color: theme.palette.normal.raisedSecondaryText
228 source: sessionChooser.icon
229 }
230
231 Keys.onReturnPressed: {
232 parent.state = "SessionsList";
233 }
234
235 onClicked: {
236 parent.state = "SessionsList";
237 }
238
239 // Refresh the icon path if looking at different places at runtime
240 // this is mainly for testing
241 Connections {
242 target: LightDMService.sessions
243 onIconSearchDirectoriesChanged: {
244 badge.source = LightDMService.sessions.iconUrl(root.currentSession)
245 }
246 }
247 }
248
249 states: [
250 State {
251 name: "SessionsList"
252 PropertyChanges { target: loginList; opacity: 0 }
253 PropertyChanges { target: sessionChooserLoader;
254 active: true;
255 opacity: 1
256 source: "SessionsList.qml"
257 }
258 },
259
260 State {
261 name: "LoginList"
262 PropertyChanges { target: loginList; opacity: 1 }
263 PropertyChanges { target: sessionChooserLoader;
264 active: false;
265 opacity: 0
266 source: "";
267 }
268 }
269 ]
270
271 transitions: [
272 Transition {
273 from: "*"
274 to: "*"
275 LomiriNumberAnimation {
276 property: "opacity";
277 }
278 }
279 ]
280 }
281}