on 8.02+15.04.20150205-0ubuntu1 help sign in
unity8/qml/Shell.qml
902 lines 32 kB history raw
1/*
2 * Copyright (C) 2013 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 */
17import QtQuick 2.0
18import QtQuick.Window 2.0
19import AccountsService 0.1
20import GSettings 1.0
21import Unity.Application 0.1
22import Ubuntu.Components 0.1
23import Ubuntu.Components.Popups 1.0
24import Ubuntu.Gestures 0.1
25import Ubuntu.SystemImage 0.1
26import Ubuntu.Telephony 0.1 as Telephony
27import Unity.Connectivity 0.1
28import Unity.Launcher 0.1
29import Utils 0.1
30import LightDM 0.1 as LightDM
31import Powerd 0.1
32import SessionBroadcast 0.1
33import "Greeter"
34import "Launcher"
35import "Panel"
36import "Components"
37import "Notifications"
38import "Stages"
39import "Wizard"
40import Unity.Notifications 1.0 as NotificationBackend
41import Unity.Session 0.1
42import Unity.DashCommunicator 0.1
43import Unity.Indicators 0.1 as Indicators
45Item {
46 id: shell
48 // Disable everything so that user can't swipe greeter or launcher until
49 // we get first prompt/authenticate, which will re-enable the shell.
50 enabled: false
52 // this is only here to select the width / height of the window if not running fullscreen
53 property bool tablet: false
54 width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
55 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
57 property real edgeSize: units.gu(2)
58 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
59 property url background: asImageTester.status == Image.Ready ? asImageTester.source
60 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
61 readonly property real panelHeight: panel.panelHeight
63 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
64 readonly property alias hasLockedApp: greeter.hasLockedApp
65 readonly property bool forcedUnlock: edgeDemo.running
66 onForcedUnlockChanged: if (forcedUnlock) lockscreen.hide()
68 property bool sideStageEnabled: shell.width >= units.gu(100)
69 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
71 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
72 property int failedLoginsDelayAttempts: 7 // number of failed logins
73 property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
75 property int orientation
76 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
77 onDeviceOrientationAngleChanged: {
78 if (!OrientationLock.enabled) {
79 orientation = Screen.orientation;
80 }
81 }
82 readonly property bool orientationLockEnabled: OrientationLock.enabled
83 onOrientationLockEnabledChanged: {
84 if (orientationLockEnabled) {
85 OrientationLock.savedOrientation = Screen.orientation;
86 } else {
87 orientation = Screen.orientation;
88 }
89 }
91 function activateApplication(appId) {
92 if (ApplicationManager.findApplication(appId)) {
93 ApplicationManager.requestFocusApplication(appId);
94 } else {
95 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
96 ApplicationManager.startApplication(appId, execFlags);
97 }
98 }
100 function startLockedApp(app) {
101 if (shell.locked) {
102 greeter.lockedApp = app;
103 }
104 shell.activateApplication(app);
105 }
107 // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
108 Image {
109 id: asImageTester
110 source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
111 height: 0
112 width: 0
113 sourceSize.height: 0
114 sourceSize.width: 0
115 }
117 GSettings {
118 id: backgroundSettings
119 schema.id: "org.gnome.desktop.background"
120 }
122 // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
123 Image {
124 id: gsImageTester
125 source: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
126 height: 0
127 width: 0
128 sourceSize.height: 0
129 sourceSize.width: 0
130 }
132 GSettings {
133 id: usageModeSettings
134 schema.id: "com.canonical.Unity8"
135 }
137 Binding {
138 target: LauncherModel
139 property: "applicationManager"
140 value: ApplicationManager
141 }
143 Component.onCompleted: {
144 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
145 if (ApplicationManager.count > 0) {
146 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
147 }
148 if (orientationLockEnabled) {
149 orientation = OrientationLock.savedOrientation;
150 }
151 }
153 VolumeControl {
154 id: volumeControl
155 }
157 DashCommunicator {
158 id: dash
159 objectName: "dashCommunicator"
160 }
162 ScreenGrabber {
163 id: screenGrabber
164 z: edgeDemo.z + 10
165 enabled: Powerd.status === Powerd.On
166 }
168 Binding {
169 target: ApplicationManager
170 property: "forceDashActive"
171 value: launcher.shown || launcher.dashSwipe
172 }
174 VolumeKeyFilter {
175 id: volumeKeyFilter
176 onVolumeDownPressed: volumeControl.volumeDown()
177 onVolumeUpPressed: volumeControl.volumeUp()
178 onBothVolumeKeysPressed: screenGrabber.capture()
179 }
181 WindowKeysFilter {
182 Keys.onPressed: {
183 // Nokia earpieces give TogglePlayPause, while the iPhone's earpiece gives Play
184 if (event.key == Qt.Key_MediaTogglePlayPause || event.key == Qt.Key_MediaPlay) {
185 event.accepted = callManager.handleMediaKey(false);
186 } else if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
187 // FIXME: We only consider power key presses if the screen is
188 // on because of bugs 1410830/1409003. The theory is that when
189 // those bugs are encountered, there is a >2s delay between the
190 // power press event and the power release event, which causes
191 // the shutdown dialog to appear on resume. So to avoid that
192 // symptom while we investigate the root cause, we simply won't
193 // initiate any dialogs when the screen is off.
194 if (Powerd.status === Powerd.On) {
195 dialogs.onPowerKeyPressed();
196 }
197 event.accepted = true;
198 } else {
199 volumeKeyFilter.onKeyPressed(event.key);
200 event.accepted = false;
201 }
202 }
204 Keys.onReleased: {
205 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
206 dialogs.onPowerKeyReleased();
207 event.accepted = true;
208 } else {
209 volumeKeyFilter.onKeyReleased(event.key);
210 event.accepted = false;
211 }
212 }
213 }
215 Item {
216 id: stages
217 objectName: "stages"
218 width: parent.width
219 height: parent.height
220 visible: !ApplicationManager.empty
222 Connections {
223 target: ApplicationManager
224 onFocusRequested: {
225 if (greeter.narrowMode) {
226 if (appId === "dialer-app" && callManager.hasCalls && shell.locked) {
227 // If we are in the middle of a call, make dialer lockedApp and show it.
228 // This can happen if user backs out of dialer back to greeter, then
229 // launches dialer again.
230 greeter.lockedApp = appId;
231 }
232 if (greeter.hasLockedApp) {
233 if (appId === greeter.lockedApp) {
234 lockscreen.hide() // show locked app
235 } else {
236 greeter.startUnlock() // show lockscreen if necessary
237 }
238 }
239 greeter.hide();
240 } else {
241 if (LightDM.Greeter.active) {
242 greeter.startUnlock()
243 }
244 }
245 }
247 onFocusedApplicationIdChanged: {
248 if (greeter.hasLockedApp && greeter.lockedApp !== ApplicationManager.focusedApplicationId) {
249 greeter.startUnlock()
250 }
251 panel.indicators.hide();
252 }
254 onApplicationAdded: {
255 if (greeter.shown && appId != "unity8-dash") {
256 greeter.startUnlock()
258 // If this happens on first boot, we may be in edge
259 // tutorial or wizard while receiving a call. But a call
260 // is more important than wizard so just bail out of those.
261 if (edgeDemo.running) {
262 edgeDemo.hideEdgeDemos();
263 wizard.hide();
264 }
265 }
266 if (greeter.narrowMode && greeter.hasLockedApp && appId === greeter.lockedApp) {
267 lockscreen.hide() // show locked app
268 }
269 launcher.hide();
270 }
271 }
273 Loader {
274 id: applicationsDisplayLoader
275 objectName: "applicationsDisplayLoader"
276 anchors.fill: parent
278 // When we have a locked app, we only want to show that one app.
279 // FIXME: do this in a less traumatic way. We currently only allow
280 // locked apps in phone mode (see FIXME in Lockscreen component in
281 // this same file). When that changes, we need to do something
282 // nicer here. But this code is currently just to prevent a
283 // theoretical attack where user enters lockedApp mode, then makes
284 // the screen larger (maybe connects to monitor) and tries to enter
285 // tablet mode.
286 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
287 source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
288 : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
290 Binding {
291 target: applicationsDisplayLoader.item
292 property: "objectName"
293 value: "stage"
294 }
295 Binding {
296 target: applicationsDisplayLoader.item
297 property: "dragAreaWidth"
298 value: shell.edgeSize
299 }
300 Binding {
301 target: applicationsDisplayLoader.item
302 property: "maximizedAppTopMargin"
303 // Not just using panel.panelHeight as that changes depending on the focused app.
304 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
305 }
306 Binding {
307 target: applicationsDisplayLoader.item
308 property: "interactive"
309 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
310 }
311 Binding {
312 target: applicationsDisplayLoader.item
313 property: "spreadEnabled"
314 value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
315 }
316 Binding {
317 target: applicationsDisplayLoader.item
318 property: "inverseProgress"
319 value: launcher.progress
320 }
321 Binding {
322 target: applicationsDisplayLoader.item
323 property: "orientation"
324 value: shell.orientation
325 }
326 Binding {
327 target: applicationsDisplayLoader.item
328 property: "background"
329 value: shell.background
330 }
331 }
332 }
334 InputMethod {
335 id: inputMethod
336 objectName: "inputMethod"
337 anchors { fill: parent; topMargin: panel.panelHeight }
338 z: notifications.useModal || panel.indicators.shown || wizard.active ? overlay.z + 1 : overlay.z - 1
339 }
341 Connections {
342 target: SurfaceManager
343 onSurfaceCreated: {
344 if (surface.type == MirSurfaceItem.InputMethod) {
345 inputMethod.surface = surface;
346 }
347 }
349 onSurfaceDestroyed: {
350 if (inputMethod.surface == surface) {
351 inputMethod.surface = null;
352 surface.parent = null;
353 }
354 if (!surface.parent) {
355 // there's no one displaying it. delete it right away
356 surface.release();
357 }
358 }
359 }
360 Connections {
361 target: SessionManager
362 onSessionStopping: {
363 if (!session.parentSession && !session.application) {
364 // nothing is using it. delete it right away
365 session.release();
366 }
367 }
368 }
370 Lockscreen {
371 id: lockscreen
372 objectName: "lockscreen"
374 hides: [launcher, panel.indicators]
375 shown: false
376 enabled: true
377 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
378 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
379 y: panel.panelHeight
380 visible: required
381 width: parent.width
382 height: parent.height - panel.panelHeight
383 background: shell.background
384 darkenBackground: 0.4
385 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
386 minPinLength: 4
387 maxPinLength: 4
389 property string promptText
390 infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
391 alphaNumeric ? i18n.tr("Enter passphrase") :
392 i18n.tr("Enter passcode")
393 errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
394 alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
395 i18n.tr("Please re-enter") :
396 i18n.tr("Sorry, incorrect passcode")
398 // FIXME: We *should* show emergency dialer if there is a SIM present,
399 // regardless of whether the side stage is enabled. But right now,
400 // the assumption is that narrow screens are phones which have SIMs
401 // and wider screens are tablets which don't. When we do allow this
402 // on devices with a side stage and a SIM, work should be done to
403 // ensure that the main stage is disabled while the dialer is present
404 // in the side stage. See the FIXME in the stage loader in this file.
405 showEmergencyCallButton: !shell.sideStageEnabled
407 onEntered: LightDM.Greeter.respond(passphrase);
408 onCancel: greeter.show()
409 onEmergencyCall: startLockedApp("dialer-app")
411 onShownChanged: if (shown) greeter.lockedApp = ""
413 function maybeShow() {
414 if (!shell.forcedUnlock && !shown) {
415 showNow();
416 }
417 }
419 Timer {
420 id: forcedDelayTimer
421 interval: 1000 * 60
422 onTriggered: {
423 if (lockscreen.delayMinutes > 0) {
424 lockscreen.delayMinutes -= 1
425 if (lockscreen.delayMinutes > 0) {
426 start() // go again
427 }
428 }
429 }
430 }
432 Component.onCompleted: {
433 if (greeter.narrowMode) {
434 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
435 }
436 }
437 }
439 Connections {
440 target: LightDM.Greeter
442 onShowGreeter: greeter.show()
443 onHideGreeter: greeter.login()
445 onShowPrompt: {
446 shell.enabled = true;
447 if (!LightDM.Greeter.active) {
448 return; // could happen if hideGreeter() comes in before we prompt
449 }
450 if (greeter.narrowMode) {
451 lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
452 lockscreen.maybeShow();
453 }
454 }
456 onPromptlessChanged: {
457 if (!LightDM.Greeter.active) {
458 return; // could happen if hideGreeter() comes in before we prompt
459 }
460 if (greeter.narrowMode) {
461 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
462 lockscreen.hide()
463 } else {
464 lockscreen.reset();
465 lockscreen.maybeShow();
466 }
467 }
468 }
470 onAuthenticationComplete: {
471 shell.enabled = true;
472 if (LightDM.Greeter.authenticated) {
473 AccountsService.failedLogins = 0
474 }
475 // Else only penalize user for a failed login if they actually were
476 // prompted for a password. We do this below after the promptless
477 // early exit.
479 if (LightDM.Greeter.promptless) {
480 return;
481 }
483 if (LightDM.Greeter.authenticated) {
484 greeter.login();
485 } else {
486 AccountsService.failedLogins++
487 if (maxFailedLogins >= 2) { // require at least a warning
488 if (AccountsService.failedLogins === maxFailedLogins - 1) {
489 var title = lockscreen.alphaNumeric ?
490 i18n.tr("Sorry, incorrect passphrase.") :
491 i18n.tr("Sorry, incorrect passcode.")
492 var text = i18n.tr("This will be your last attempt.") + " " +
493 (lockscreen.alphaNumeric ?
494 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
495 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
496 lockscreen.showInfoPopup(title, text)
497 } else if (AccountsService.failedLogins >= maxFailedLogins) {
498 SystemImage.factoryReset() // Ouch!
499 }
500 }
501 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
502 lockscreen.delayMinutes = failedLoginsDelayMinutes
503 forcedDelayTimer.start()
504 }
506 lockscreen.clear(true);
507 if (greeter.narrowMode) {
508 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
509 }
510 }
511 }
512 }
514 Binding {
515 target: LightDM.Greeter
516 property: "active"
517 value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
518 }
520 Rectangle {
521 anchors.fill: parent
522 color: "black"
523 opacity: greeterWrapper.showProgress * 0.8
524 }
526 Item {
527 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
528 id: greeterWrapper
529 objectName: "greeterWrapper"
530 x: (greeter.narrowMode && greeter.showProgress > 0) ? launcher.progress : 0
531 y: panel.panelHeight
532 width: parent.width
533 height: parent.height - panel.panelHeight
535 Behavior on x {
536 enabled: !launcher.dashSwipe
537 StandardAnimation {}
538 }
540 property bool fullyShown: showProgress === 1.0
541 onFullyShownChanged: {
542 // Wait until the greeter is completely covering lockscreen before resetting it.
543 if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
544 lockscreen.reset();
545 lockscreen.maybeShow();
546 }
547 }
549 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
550 onShowProgressChanged: {
551 if (showProgress === 0) {
552 if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) {
553 greeter.login()
554 } else if (greeter.narrowMode) {
555 lockscreen.clear(false) // to reset focus if necessary
556 }
557 }
558 }
560 Greeter {
561 id: greeter
562 objectName: "greeter"
564 signal sessionStarted() // helpful for tests
566 property string lockedApp: ""
567 property bool hasLockedApp: lockedApp !== ""
569 hides: [launcher, panel.indicators]
570 loadContent: required || lockscreen.required // keeps content in memory for quick show()
572 locked: shell.locked
574 background: shell.background
576 width: parent.width
577 height: parent.height
580 // avoid overlapping with Launcher's edge drag area
581 // FIXME: Fix TouchRegistry & friends and remove this workaround
582 // Issue involves launcher's DDA getting disabled on a long
583 // left-edge drag
584 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
586 function startUnlock() {
587 if (narrowMode) {
588 if (!LightDM.Greeter.authenticated) {
589 lockscreen.maybeShow()
590 }
591 hide()
592 } else {
593 show()
594 tryToUnlock()
595 }
596 }
598 function login() {
599 enabled = false;
600 if (LightDM.Greeter.startSessionSync()) {
601 sessionStarted();
602 greeter.hide();
603 lockscreen.hide();
604 launcher.hide();
605 }
606 enabled = true;
607 }
609 Timer {
610 // See powerConnection for why this is useful
611 id: showGreeterDelayed
612 interval: 1
613 onTriggered: {
614 greeter.showNow();
615 }
616 }
618 onShownChanged: {
619 if (shown) {
620 // Disable everything so that user can't swipe greeter or
621 // launcher until we get the next prompt/authenticate, which
622 // will re-enable the shell.
623 shell.enabled = false;
625 if (greeter.narrowMode) {
626 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
627 } else {
628 reset()
629 }
630 greeter.lockedApp = "";
631 greeter.forceActiveFocus();
632 }
633 }
635 Component.onCompleted: {
636 Connectivity.unlockAllModems()
637 }
639 onUnlocked: greeter.hide()
640 onSelected: {
641 // Update launcher items for new user
642 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
643 AccountsService.user = user;
644 LauncherModel.setUser(user);
645 }
647 onTapped: launcher.tease()
648 onDraggingChanged: {
649 if (dragging) {
650 launcher.tease();
651 }
652 }
654 Binding {
655 target: ApplicationManager
656 property: "suspended"
657 value: (greeter.shown && greeterWrapper.showProgress == 1) || lockscreen.shown
658 }
659 }
660 }
662 Connections {
663 id: callConnection
664 target: callManager
666 onHasCallsChanged: {
667 if (shell.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
668 // We just received an incoming call while locked. The
669 // indicator will have already launched dialer-app for us, but
670 // there is a race between "hasCalls" changing and the dialer
671 // starting up. So in case we lose that race, we'll start/
672 // focus the dialer ourselves here too. Even if the indicator
673 // didn't launch the dialer for some reason (or maybe a call
674 // started via some other means), if an active call is
675 // happening, we want to be in the dialer.
676 startLockedApp("dialer-app")
677 }
678 }
679 }
681 Connections {
682 id: powerConnection
683 target: Powerd
685 onStatusChanged: {
686 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
687 !callManager.hasCalls && !edgeDemo.running) {
688 // We don't want to simply call greeter.showNow() here, because
689 // that will take too long. Qt will delay button event
690 // handling until the greeter is done loading and may think the
691 // user held down the power button the whole time, leading to a
692 // power dialog being shown. Instead, delay showing the
693 // greeter until we've finished handling the event. We could
694 // make the greeter load asynchronously instead, but that
695 // introduces a whole host of timing issues, especially with
696 // its animations. So this is simpler.
697 showGreeterDelayed.start();
698 }
699 }
700 }
702 function showHome() {
703 if (edgeDemo.running) {
704 return
705 }
707 if (LightDM.Greeter.active) {
708 greeter.startUnlock()
709 }
711 var animate = !LightDM.Greeter.active && !stages.shown
712 dash.setCurrentScope(0, animate, false)
713 ApplicationManager.requestFocusApplication("unity8-dash")
714 }
716 function showDash() {
717 if (greeter.hasLockedApp || // just in case user gets here
718 (!greeter.narrowMode && shell.locked)) {
719 return
720 }
722 if (greeter.shown) {
723 greeter.hideRight();
724 launcher.fadeOut();
725 }
727 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
728 ApplicationManager.requestFocusApplication("unity8-dash")
729 launcher.fadeOut();
730 }
731 }
733 Item {
734 id: overlay
735 z: 10
737 anchors.fill: parent
739 Panel {
740 id: panel
741 objectName: "panel"
742 anchors.fill: parent //because this draws indicator menus
743 indicators {
744 hides: [launcher]
745 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
746 contentEnabled: edgeDemo.panelContentEnabled
747 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
749 minimizedPanelHeight: units.gu(3)
750 expandedPanelHeight: units.gu(7)
752 indicatorsModel: Indicators.IndicatorsModel {
753 Component.onCompleted: load(indicatorProfile);
754 }
755 }
756 callHint {
757 greeterShown: greeter.shown || lockscreen.shown
758 }
760 property bool topmostApplicationIsFullscreen:
761 ApplicationManager.focusedApplicationId &&
762 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
764 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
765 || greeter.hasLockedApp
766 }
768 Launcher {
769 id: launcher
770 objectName: "launcher"
772 readonly property bool dashSwipe: progress > 0
774 anchors.top: parent.top
775 anchors.topMargin: inverted ? 0 : panel.panelHeight
776 anchors.bottom: parent.bottom
777 width: parent.width
778 dragAreaWidth: shell.edgeSize
779 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
780 inverted: usageModeSettings.usageMode === "Staged"
782 onShowDashHome: showHome()
783 onDash: showDash()
784 onDashSwipeChanged: {
785 if (dashSwipe) {
786 dash.setCurrentScope(0, false, true)
787 }
788 }
789 onLauncherApplicationSelected: {
790 if (greeter.hasLockedApp) {
791 greeter.startUnlock()
792 }
793 if (!edgeDemo.running)
794 shell.activateApplication(appId)
795 }
796 onShownChanged: {
797 if (shown) {
798 panel.indicators.hide()
799 }
800 }
801 }
803 Wizard {
804 id: wizard
805 anchors.fill: parent
806 background: shell.background
807 }
809 Rectangle {
810 id: modalNotificationBackground
812 visible: notifications.useModal && (notifications.state == "narrow")
813 color: "#000000"
814 anchors.fill: parent
815 opacity: 0.9
817 MouseArea {
818 anchors.fill: parent
819 }
820 }
822 Notifications {
823 id: notifications
825 model: NotificationBackend.Model
826 margin: units.gu(1)
828 y: topmostIsFullscreen ? 0 : panel.panelHeight
829 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
831 states: [
832 State {
833 name: "narrow"
834 when: overlay.width <= units.gu(60)
835 AnchorChanges {
836 target: notifications
837 anchors.left: parent.left
838 anchors.right: parent.right
839 }
840 },
841 State {
842 name: "wide"
843 when: overlay.width > units.gu(60)
844 AnchorChanges {
845 target: notifications
846 anchors.left: undefined
847 anchors.right: parent.right
848 }
849 PropertyChanges { target: notifications; width: units.gu(38) }
850 }
851 ]
852 }
853 }
855 Dialogs {
856 id: dialogs
857 anchors.fill: parent
858 z: overlay.z + 10
859 onPowerOffClicked: {
860 shutdownFadeOutRectangle.enabled = true;
861 shutdownFadeOutRectangle.visible = true;
862 shutdownFadeOut.start();
863 }
864 }
866 EdgeDemo {
867 id: edgeDemo
868 objectName: "edgeDemo"
869 z: dialogs.z + 10
870 paused: Powerd.status === Powerd.Off || wizard.active // Saves power
871 greeter: greeter
872 launcher: launcher
873 panel: panel
874 stages: stages
875 }
877 Connections {
878 target: SessionBroadcast
879 onShowHome: showHome()
880 }
882 Rectangle {
883 id: shutdownFadeOutRectangle
884 z: edgeDemo.z + 10
885 enabled: false
886 visible: false
887 color: "black"
888 anchors.fill: parent
889 opacity: 0.0
890 NumberAnimation on opacity {
891 id: shutdownFadeOut
892 from: 0.0
893 to: 1.0
894 onStopped: {
895 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
896 DBusUnitySessionService.Shutdown();
897 }
898 }
899 }
900 }