Unity 8
GreeterView.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 
18 import QtQuick 2.12
19 import QtQuick.Window 2.2
20 import QtGraphicalEffects 1.12
21 import Ubuntu.Components 1.3
22 import Ubuntu.Telephony 0.1 as Telephony
23 import "../Components"
24 
25 FocusScope {
26  id: root
27  objectName: "GreeterView"
28 
29  focus: true
30 
31  property url background
32  property real backgroundSourceSize
33  property real panelHeight
34  property bool hasCustomBackground
35  property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin
36  property var infographicModel
37  property alias launcherOffset: coverPage.launcherOffset
38  property alias currentIndex: loginList.currentIndex
39  property alias delayMinutes: delayedLockscreen.delayMinutes // TODO
40  property alias alphanumeric: loginList.alphanumeric
41  property alias hasKeyboard: loginList.hasKeyboard
42  property bool locked
43  property bool waiting
44  property var userModel // Set from outside
45  property bool multiUser: false
46  property int orientation
47  property bool isLandscape: root.orientation == Qt.LandscapeOrientation ||
48  root.orientation == Qt.InvertedLandscapeOrientation ||
49  usageMode == "desktop"
50  property bool isPortrait: (root.orientation == Qt.PortraitOrientation ||
51  root.orientation == Qt.InvertedPortraitOrientation) &&
52  usageMode != "desktop"
53 
54  property string usageMode
55 
56  readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
57  readonly property bool fullyShown: coverPage.showProgress === 1 || lockscreen.shown
58  readonly property bool required: coverPage.required || lockscreen.required
59  readonly property alias sessionToStart: loginList.currentSession
60 
61  property rect inputMethodRect
62 
63  signal selected(int index)
64  signal responded(string response)
65  signal tease()
66  signal emergencyCall() // unused
67 
68  function notifyAuthenticationFailed() {
69  loginList.showError();
70  }
71 
72  function forceShow() {
73  coverPage.show();
74  }
75 
76  function tryToUnlock(toTheRight) {
77  var coverChanged = coverPage.shown;
78  if (toTheRight) {
79  coverPage.hideRight();
80  } else {
81  coverPage.hide();
82  }
83  if (root.locked) {
84  lockscreen.show();
85  loginList.tryToUnlock();
86  return false;
87  } else {
88  root.responded("");
89  return coverChanged;
90  }
91  }
92 
93  function hide() {
94  lockscreen.hide();
95  coverPage.hide();
96  }
97 
98  function showFakePassword() {
99  loginList.showFakePassword();
100  }
101 
102  function showErrorMessage(msg) {
103  coverPage.showErrorMessage(msg);
104  }
105 
106  onLockedChanged: changeLockscreenState()
107  onMultiUserChanged: changeLockscreenState()
108 
109  function changeLockscreenState() {
110  if (locked || multiUser) {
111  lockscreen.maybeShow();
112  } else {
113  lockscreen.hide();
114  }
115  }
116 
117  Keys.onSpacePressed: coverPage.hide();
118  Keys.onReturnPressed: coverPage.hide();
119  Keys.onEnterPressed: coverPage.hide();
120 
121  Rectangle {
122  anchors.fill: parent
123  color: "black"
124  opacity: lockscreen.showProgress * 0.8
125  }
126 
127  CoverPage {
128  id: lockscreen
129  objectName: "lockscreen"
130  height: parent.height
131  width: parent.width
132  draggable: false
133  state: "LoginList"
134 
135  background: root.background
136  backgroundSourceSize: root.backgroundSourceSize
137  panelHeight: root.panelHeight
138  hasCustomBackground: root.hasCustomBackground
139  backgroundShadeOpacity: 0.6
140 
141  showInfographic: isLandscape && root.usageMode != "phone" && (root.usageMode != "tablet" || root.multiUser) && !delayedLockscreen.visible
142  infographicModel: root.infographicModel
143 
144  shown: false
145  opacity: 0
146 
147  showAnimation: StandardAnimation { property: "opacity"; to: 1 }
148  hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
149 
150  infographicsTopMargin: parent.height * 0.125
151  infographicsBottomMargin: parent.height * 0.125
152  infographicsLeftMargin: loginList.x + loginList.width
153 
154  onTease: root.tease()
155 
156  onShowProgressChanged: {
157  if (showProgress === 0 && !root.locked) {
158  root.responded("");
159  }
160  }
161 
162  LoginList {
163  id: loginList
164  objectName: "loginList"
165 
166  width: units.gu(40)
167  anchors {
168  top: parent.top
169  bottom: parent.bottom
170  }
171 
172  boxVerticalOffset: (height - highlightedHeight -
173  inputMethodRect.height) / 2
174  Behavior on boxVerticalOffset { UbuntuNumberAnimation {} }
175 
176  enabled: !coverPage.shown && visible
177  visible: !delayedLockscreen.visible
178 
179  model: root.userModel
180  onResponded: root.responded(response)
181  onSelected: root.selected(index)
182  onSessionChooserButtonClicked: parent.state = "SessionsList"
183  onCurrentIndexChanged: setCurrentSession()
184 
185  locked: root.locked
186  waiting: root.waiting
187 
188  Keys.forwardTo: [sessionChooserLoader.item]
189 
190  Component.onCompleted: setCurrentSession()
191 
192  function setCurrentSession() {
193  currentSession = LightDMService.users.data(currentIndex, LightDMService.userRoles.SessionRole);
194  }
195  }
196 
197  DelayedLockscreen {
198  id: delayedLockscreen
199  objectName: "delayedLockscreen"
200  anchors.fill: parent
201  visible: delayMinutes > 0
202  alphaNumeric: loginList.alphanumeric
203  }
204 
205  function maybeShow() {
206  if ((root.locked || root.multiUser) && !shown) {
207  showNow();
208  }
209  }
210 
211  Loader {
212  id: sessionChooserLoader
213 
214  height: loginList.height
215  width: loginList.width
216 
217  anchors {
218  left: parent.left
219  leftMargin: Math.min(parent.width * 0.16, units.gu(20))
220  top: parent.top
221  }
222 
223  active: false
224 
225  onLoaded: sessionChooserLoader.item.forceActiveFocus();
226  onActiveChanged: {
227  if (!active) return;
228  item.updateHighlight(loginList.currentSession);
229  }
230 
231  Connections {
232  target: sessionChooserLoader.item
233  onSessionSelected: loginList.currentSession = sessionKey
234  onShowLoginList: {
235  lockscreen.state = "LoginList"
236  loginList.tryToUnlock();
237  }
238  ignoreUnknownSignals: true
239  }
240  }
241 
242  // Use an AbstractButton due to icon limitations with Button
243  AbstractButton {
244  id: sessionChooser
245  objectName: "sessionChooserButton"
246 
247  readonly property url icon: LightDMService.sessions.iconUrl(loginList.currentSession)
248 
249  visible: LightDMService.sessions.count > 1 &&
250  !LightDMService.users.data(loginList.currentUserIndex, LightDMService.userRoles.LoggedInRole)
251 
252  height: units.gu(3.5)
253  width: units.gu(3.5)
254 
255  activeFocusOnTab: true
256  anchors {
257  right: parent.right
258  rightMargin: units.gu(2)
259 
260  bottom: parent.bottom
261  bottomMargin: units.gu(1.5)
262  }
263 
264  Rectangle {
265  id: badgeHighlight
266 
267  anchors.fill: parent
268  visible: parent.activeFocus
269  color: "transparent"
270  border.color: theme.palette.normal.focus
271  border.width: units.dp(1)
272  radius: 3
273  }
274 
275  Icon {
276  id: badge
277  anchors.fill: parent
278  anchors.margins: units.dp(3)
279  keyColor: "#ffffff" // icon providers give us white icons
280  color: theme.palette.normal.raisedSecondaryText
281  source: sessionChooser.icon
282  }
283 
284  Keys.onReturnPressed: {
285  parent.state = "SessionsList";
286  }
287 
288  onClicked: {
289  parent.state = "SessionsList";
290  }
291 
292  // Refresh the icon path if looking at different places at runtime
293  // this is mainly for testing
294  Connections {
295  target: LightDMService.sessions
296  onIconSearchDirectoriesChanged: {
297  badge.source = LightDMService.sessions.iconUrl(root.currentSession)
298  }
299  }
300  }
301 
302  states: [
303  State {
304  name: "SessionsList"
305  PropertyChanges { target: loginList; opacity: 0 }
306  PropertyChanges { target: sessionChooserLoader;
307  active: true;
308  opacity: 1
309  source: "SessionsList.qml"
310  }
311  },
312 
313  State {
314  name: "LoginList"
315  PropertyChanges { target: loginList; opacity: 1 }
316  PropertyChanges { target: sessionChooserLoader;
317  active: false;
318  opacity: 0
319  source: "";
320  }
321  }
322  ]
323 
324  transitions: [
325  Transition {
326  from: "*"
327  to: "*"
328  UbuntuNumberAnimation {
329  property: "opacity";
330  }
331  }
332  ]
333 
334  Component.onCompleted: if (root.multiUser) showNow()
335  }
336 
337  Rectangle {
338  anchors.fill: parent
339  color: "black"
340  opacity: coverPage.showProgress * 0.8
341  }
342 
343  CoverPage {
344  id: coverPage
345  objectName: "coverPage"
346  height: parent.height
347  width: parent.width
348  background: root.background
349  hasCustomBackground: root.hasCustomBackground
350  backgroundShadeOpacity: 0.4
351  panelHeight: root.panelHeight
352  draggable: !root.waiting
353  onTease: root.tease()
354  onClicked: hide()
355  backgroundSourceSize: root.backgroundSourceSize
356  infographicModel: root.infographicModel
357 
358  showInfographic: !root.multiUser && root.usageMode != "desktop"
359 
360  onShowProgressChanged: {
361  if (showProgress === 0) {
362  if (lockscreen.shown) {
363  loginList.tryToUnlock();
364  } else {
365  root.responded("");
366  }
367  }
368  }
369 
370  Clock {
371  id: clock
372  anchors.centerIn: parent
373  }
374 
375  states: [
376  State {
377  name: "landscape-with-infographics"
378  when: isLandscape && coverPage.showInfographic
379  AnchorChanges {
380  target: clock
381  anchors.top: undefined
382  anchors.horizontalCenter: undefined
383  anchors.verticalCenter: undefined
384  }
385  PropertyChanges {
386  target: clock;
387  anchors.topMargin: undefined
388  anchors.centerIn: coverPage
389  anchors.horizontalCenterOffset: - coverPage.width / 2 + clock.width / 2 + units.gu(8)
390  }
391  PropertyChanges {
392  target: coverPage
393  infographicsLeftMargin: clock.width + units.gu(8)
394  }
395  },
396  State {
397  name: "portrait"
398  when: isPortrait && coverPage.showInfographic
399  AnchorChanges {
400  target: clock;
401  anchors.top: coverPage.top
402  anchors.horizontalCenter: coverPage.horizontalCenter
403  anchors.verticalCenter: undefined
404  }
405  PropertyChanges {
406  target: clock;
407  anchors.topMargin: units.gu(2) + panelHeight
408  anchors.centerIn: undefined
409  anchors.horizontalCenterOffset: 0
410  }
411  PropertyChanges {
412  target: coverPage
413  infographicsLeftMargin: 0
414  }
415  },
416  State {
417  name: "without-infographics"
418  when: !coverPage.showInfographic
419  AnchorChanges {
420  target: clock
421  anchors.top: undefined
422  anchors.horizontalCenter: coverPage.horizontalCenter
423  anchors.verticalCenter: coverPage.verticalCenter
424  }
425  PropertyChanges {
426  target: clock;
427  anchors.topMargin: 0
428  anchors.centerIn: undefined
429  anchors.horizontalCenterOffset: 0
430  }
431  PropertyChanges {
432  target: coverPage
433  infographicsLeftMargin: 0
434  }
435  }
436  ]
437  }
438 
439  StyledItem {
440  id: bottomBar
441  visible: usageMode == "phone" && lockscreen.shown
442  height: units.gu(4)
443 
444  anchors.left: parent.left
445  anchors.right: parent.right
446  anchors.top: parent.bottom
447  anchors.topMargin: - height * (1 - coverPage.showProgress)
448  - ( inputMethodRect.height )
449 
450  Label {
451  text: i18n.tr("Cancel")
452  anchors.left: parent.left
453  anchors.leftMargin: units.gu(2)
454  anchors.top: parent.top
455  anchors.bottom: parent.bottom
456  verticalAlignment: Text.AlignVCenter
457  font.weight: Font.Light
458  fontSize: "small"
459  color: theme.palette.normal.raisedSecondaryText
460 
461  AbstractButton {
462  anchors.fill: parent
463  anchors.leftMargin: -units.gu(2)
464  anchors.rightMargin: -units.gu(2)
465  onClicked: coverPage.show()
466  }
467  }
468 
469  Label {
470  objectName: "emergencyCallLabel"
471  text: callManager.hasCalls ? i18n.tr("Return to Call") : i18n.tr("Emergency")
472  anchors.right: parent.right
473  anchors.rightMargin: units.gu(2)
474  anchors.top: parent.top
475  anchors.bottom: parent.bottom
476  verticalAlignment: Text.AlignVCenter
477  font.weight: Font.Light
478  fontSize: "small"
479  color: theme.palette.normal.raisedSecondaryText
480  // TODO: uncomment once bug 1616538 is fixed
481  // visible: telepathyHelper.ready && telepathyHelper.emergencyCallsAvailable
482  enabled: visible
483 
484  AbstractButton {
485  anchors.fill: parent
486  anchors.leftMargin: -units.gu(2)
487  anchors.rightMargin: -units.gu(2)
488  onClicked: root.emergencyCall()
489  }
490  }
491  }
492 
493  states: [
494  State {
495  name: "phone"
496  when: root.usageMode == "phone" || (root.usageMode == "tablet" && isPortrait)
497  AnchorChanges {
498  target: loginList;
499  anchors.horizontalCenter: lockscreen.horizontalCenter;
500  anchors.left: undefined;
501  }
502  PropertyChanges {
503  target: loginList;
504  anchors.leftMargin: 0;
505  }
506  },
507  State {
508  name: "tablet"
509  when: root.usageMode == "tablet" && isLandscape
510  AnchorChanges {
511  target: loginList;
512  anchors.horizontalCenter: undefined;
513  anchors.left: lockscreen.left;
514  }
515  PropertyChanges {
516  target: loginList;
517  anchors.leftMargin: Math.min(lockscreen.width * 0.16, units.gu(8));
518  }
519  },
520  State {
521  name: "desktop"
522  when: root.usageMode == "desktop"
523  AnchorChanges {
524  target: loginList;
525  anchors.horizontalCenter: undefined;
526  anchors.left: lockscreen.left;
527  }
528  PropertyChanges {
529  target: loginList;
530  anchors.leftMargin: Math.min(lockscreen.width * 0.16, units.gu(20));
531  }
532  }
533  ]
534 }