on bd49d29 help sign in
unity8/qml/Shell.qml
894 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 "Panel/Indicators"
40import "Wizard"
41import Unity.Notifications 1.0 as NotificationBackend
42import Unity.Session 0.1
43import Unity.DashCommunicator 0.1
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 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
184 dialogs.onPowerKeyPressed();
185 event.accepted = true;
186 } else {
187 volumeKeyFilter.onKeyPressed(event.key);
188 event.accepted = false;
189 }
190 }
192 Keys.onReleased: {
193 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
194 dialogs.onPowerKeyReleased();
195 event.accepted = true;
196 } else {
197 volumeKeyFilter.onKeyReleased(event.key);
198 event.accepted = false;
199 }
200 }
201 }
203 Item {
204 id: stages
205 objectName: "stages"
206 width: parent.width
207 height: parent.height
208 visible: !ApplicationManager.empty
210 Connections {
211 target: ApplicationManager
212 onFocusRequested: {
213 if (greeter.narrowMode) {
214 if (appId === "dialer-app" && callManager.hasCalls && shell.locked) {
215 // If we are in the middle of a call, make dialer lockedApp and show it.
216 // This can happen if user backs out of dialer back to greeter, then
217 // launches dialer again.
218 greeter.lockedApp = appId;
219 }
220 if (greeter.hasLockedApp) {
221 if (appId === greeter.lockedApp) {
222 lockscreen.hide() // show locked app
223 } else {
224 greeter.startUnlock() // show lockscreen if necessary
225 }
226 }
227 greeter.hide();
228 } else {
229 if (LightDM.Greeter.active) {
230 greeter.startUnlock()
231 }
232 }
233 }
235 onFocusedApplicationIdChanged: {
236 if (greeter.hasLockedApp && greeter.lockedApp !== ApplicationManager.focusedApplicationId) {
237 greeter.startUnlock()
238 }
239 panel.indicators.hide();
240 }
242 onApplicationAdded: {
243 if (greeter.shown && appId != "unity8-dash") {
244 greeter.startUnlock()
246 // If this happens on first boot, we may be in edge
247 // tutorial or wizard while receiving a call. But a call
248 // is more important than wizard so just bail out of those.
249 if (edgeDemo.running) {
250 edgeDemo.hideEdgeDemos();
251 wizard.hide();
252 }
253 }
254 if (greeter.narrowMode && greeter.hasLockedApp && appId === greeter.lockedApp) {
255 lockscreen.hide() // show locked app
256 }
257 launcher.hide();
258 }
259 }
261 Loader {
262 id: applicationsDisplayLoader
263 objectName: "applicationsDisplayLoader"
264 anchors.fill: parent
266 // When we have a locked app, we only want to show that one app.
267 // FIXME: do this in a less traumatic way. We currently only allow
268 // locked apps in phone mode (see FIXME in Lockscreen component in
269 // this same file). When that changes, we need to do something
270 // nicer here. But this code is currently just to prevent a
271 // theoretical attack where user enters lockedApp mode, then makes
272 // the screen larger (maybe connects to monitor) and tries to enter
273 // tablet mode.
274 property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
275 source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
276 : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
278 Binding {
279 target: applicationsDisplayLoader.item
280 property: "objectName"
281 value: "stage"
282 }
283 Binding {
284 target: applicationsDisplayLoader.item
285 property: "dragAreaWidth"
286 value: shell.edgeSize
287 }
288 Binding {
289 target: applicationsDisplayLoader.item
290 property: "maximizedAppTopMargin"
291 // Not just using panel.panelHeight as that changes depending on the focused app.
292 value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
293 }
294 Binding {
295 target: applicationsDisplayLoader.item
296 property: "interactive"
297 value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
298 }
299 Binding {
300 target: applicationsDisplayLoader.item
301 property: "spreadEnabled"
302 value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
303 }
304 Binding {
305 target: applicationsDisplayLoader.item
306 property: "inverseProgress"
307 value: launcher.progress
308 }
309 Binding {
310 target: applicationsDisplayLoader.item
311 property: "orientation"
312 value: shell.orientation
313 }
314 Binding {
315 target: applicationsDisplayLoader.item
316 property: "background"
317 value: shell.background
318 }
319 }
320 }
322 InputMethod {
323 id: inputMethod
324 objectName: "inputMethod"
325 anchors { fill: parent; topMargin: panel.panelHeight }
326 z: notifications.useModal || panel.indicators.shown || wizard.active ? overlay.z + 1 : overlay.z - 1
327 }
329 Connections {
330 target: SurfaceManager
331 onSurfaceCreated: {
332 if (surface.type == MirSurfaceItem.InputMethod) {
333 inputMethod.surface = surface;
334 }
335 }
337 onSurfaceDestroyed: {
338 if (inputMethod.surface == surface) {
339 inputMethod.surface = null;
340 surface.parent = null;
341 }
342 if (!surface.parent) {
343 // there's no one displaying it. delete it right away
344 surface.release();
345 }
346 }
347 }
348 Connections {
349 target: SessionManager
350 onSessionStopping: {
351 if (!session.parentSession && !session.application) {
352 // nothing is using it. delete it right away
353 session.release();
354 }
355 }
356 }
358 Lockscreen {
359 id: lockscreen
360 objectName: "lockscreen"
362 hides: [launcher, panel.indicators]
363 shown: false
364 enabled: true
365 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
366 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
367 y: panel.panelHeight
368 visible: required
369 width: parent.width
370 height: parent.height - panel.panelHeight
371 background: shell.background
372 darkenBackground: 0.4
373 alphaNumeric: AccountsService.passwordDisplayHint === AccountsService.Keyboard
374 minPinLength: 4
375 maxPinLength: 4
377 property string promptText
378 infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
379 alphaNumeric ? i18n.tr("Enter passphrase") :
380 i18n.tr("Enter passcode")
381 errorText: promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
382 alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
383 i18n.tr("Please re-enter") :
384 i18n.tr("Sorry, incorrect passcode")
386 // FIXME: We *should* show emergency dialer if there is a SIM present,
387 // regardless of whether the side stage is enabled. But right now,
388 // the assumption is that narrow screens are phones which have SIMs
389 // and wider screens are tablets which don't. When we do allow this
390 // on devices with a side stage and a SIM, work should be done to
391 // ensure that the main stage is disabled while the dialer is present
392 // in the side stage. See the FIXME in the stage loader in this file.
393 showEmergencyCallButton: !shell.sideStageEnabled
395 onEntered: LightDM.Greeter.respond(passphrase);
396 onCancel: greeter.show()
397 onEmergencyCall: startLockedApp("dialer-app")
399 onShownChanged: if (shown) greeter.lockedApp = ""
401 function maybeShow() {
402 if (!shell.forcedUnlock) {
403 showNow();
404 }
405 }
407 Timer {
408 id: forcedDelayTimer
409 interval: 1000 * 60
410 onTriggered: {
411 if (lockscreen.delayMinutes > 0) {
412 lockscreen.delayMinutes -= 1
413 if (lockscreen.delayMinutes > 0) {
414 start() // go again
415 }
416 }
417 }
418 }
420 Component.onCompleted: {
421 if (greeter.narrowMode) {
422 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
423 }
424 }
425 }
427 Connections {
428 target: LightDM.Greeter
430 onShowGreeter: greeter.show()
431 onHideGreeter: greeter.login()
433 onShowPrompt: {
434 shell.enabled = true;
435 if (!LightDM.Greeter.active) {
436 return; // could happen if hideGreeter() comes in before we prompt
437 }
438 if (greeter.narrowMode) {
439 lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
440 lockscreen.maybeShow();
441 }
442 }
444 onPromptlessChanged: {
445 if (!LightDM.Greeter.active) {
446 return; // could happen if hideGreeter() comes in before we prompt
447 }
448 if (greeter.narrowMode) {
449 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
450 lockscreen.hide()
451 } else {
452 lockscreen.reset();
453 lockscreen.maybeShow();
454 }
455 }
456 }
458 onAuthenticationComplete: {
459 shell.enabled = true;
460 if (LightDM.Greeter.authenticated) {
461 AccountsService.failedLogins = 0
462 }
463 // Else only penalize user for a failed login if they actually were
464 // prompted for a password. We do this below after the promptless
465 // early exit.
467 if (LightDM.Greeter.promptless) {
468 return;
469 }
471 if (LightDM.Greeter.authenticated) {
472 greeter.login();
473 } else {
474 AccountsService.failedLogins++
475 if (maxFailedLogins >= 2) { // require at least a warning
476 if (AccountsService.failedLogins === maxFailedLogins - 1) {
477 var title = lockscreen.alphaNumeric ?
478 i18n.tr("Sorry, incorrect passphrase.") :
479 i18n.tr("Sorry, incorrect passcode.")
480 var text = i18n.tr("This will be your last attempt.") + " " +
481 (lockscreen.alphaNumeric ?
482 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
483 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
484 lockscreen.showInfoPopup(title, text)
485 } else if (AccountsService.failedLogins >= maxFailedLogins) {
486 SystemImage.factoryReset() // Ouch!
487 }
488 }
489 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
490 lockscreen.delayMinutes = failedLoginsDelayMinutes
491 forcedDelayTimer.start()
492 }
494 lockscreen.clear(true);
495 if (greeter.narrowMode) {
496 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
497 }
498 }
499 }
500 }
502 Binding {
503 target: LightDM.Greeter
504 property: "active"
505 value: greeter.shown || lockscreen.shown || greeter.hasLockedApp
506 }
508 Rectangle {
509 anchors.fill: parent
510 color: "black"
511 opacity: greeterWrapper.showProgress * 0.8
512 }
514 Item {
515 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
516 id: greeterWrapper
517 objectName: "greeterWrapper"
518 x: (greeter.narrowMode && greeter.showProgress > 0) ? launcher.progress : 0
519 y: panel.panelHeight
520 width: parent.width
521 height: parent.height - panel.panelHeight
523 Behavior on x {
524 enabled: !launcher.dashSwipe
525 StandardAnimation {}
526 }
528 property bool fullyShown: showProgress === 1.0
529 onFullyShownChanged: {
530 // Wait until the greeter is completely covering lockscreen before resetting it.
531 if (greeter.narrowMode && fullyShown && !LightDM.Greeter.authenticated) {
532 lockscreen.reset();
533 lockscreen.maybeShow();
534 }
535 }
537 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
538 onShowProgressChanged: {
539 if (showProgress === 0) {
540 if ((LightDM.Greeter.promptless && LightDM.Greeter.authenticated) || shell.forcedUnlock) {
541 greeter.login()
542 } else if (greeter.narrowMode) {
543 lockscreen.clear(false) // to reset focus if necessary
544 }
545 }
546 }
548 Greeter {
549 id: greeter
550 objectName: "greeter"
552 signal sessionStarted() // helpful for tests
554 property string lockedApp: ""
555 property bool hasLockedApp: lockedApp !== ""
557 hides: [launcher, panel.indicators]
558 loadContent: required || lockscreen.required // keeps content in memory for quick show()
560 locked: shell.locked
562 background: shell.background
564 width: parent.width
565 height: parent.height
568 // avoid overlapping with Launcher's edge drag area
569 // FIXME: Fix TouchRegistry & friends and remove this workaround
570 // Issue involves launcher's DDA getting disabled on a long
571 // left-edge drag
572 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
574 function startUnlock() {
575 if (narrowMode) {
576 if (!LightDM.Greeter.authenticated) {
577 lockscreen.maybeShow()
578 }
579 hide()
580 } else {
581 show()
582 tryToUnlock()
583 }
584 }
586 function login() {
587 enabled = false;
588 if (LightDM.Greeter.startSessionSync()) {
589 sessionStarted();
590 greeter.hide();
591 lockscreen.hide();
592 launcher.hide();
593 }
594 enabled = true;
595 }
597 Timer {
598 // See powerConnection for why this is useful
599 id: showGreeterDelayed
600 interval: 1
601 onTriggered: {
602 greeter.showNow();
603 }
604 }
606 onShownChanged: {
607 if (shown) {
608 // Disable everything so that user can't swipe greeter or
609 // launcher until we get the next prompt/authenticate, which
610 // will re-enable the shell.
611 shell.enabled = false;
613 if (greeter.narrowMode) {
614 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
615 } else {
616 reset()
617 }
618 greeter.lockedApp = "";
619 greeter.forceActiveFocus();
620 }
621 }
623 Component.onCompleted: {
624 Connectivity.unlockAllModems()
625 }
627 onUnlocked: greeter.hide()
628 onSelected: {
629 // Update launcher items for new user
630 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
631 AccountsService.user = user;
632 LauncherModel.setUser(user);
633 }
635 onTapped: launcher.tease()
636 onDraggingChanged: {
637 if (dragging) {
638 launcher.tease();
639 }
640 }
642 Binding {
643 target: ApplicationManager
644 property: "suspended"
645 value: (greeter.shown && greeterWrapper.showProgress == 1) || lockscreen.shown
646 }
647 }
648 }
650 Connections {
651 id: callConnection
652 target: callManager
654 onHasCallsChanged: {
655 if (shell.locked && callManager.hasCalls) {
656 // We just received an incoming call while locked. The
657 // indicator will have already launched dialer-app for us, but
658 // there is a race between "hasCalls" changing and the dialer
659 // starting up. So in case we lose that race, we'll start/
660 // focus the dialer ourselves here too. Even if the indicator
661 // didn't launch the dialer for some reason (or maybe a call
662 // started via some other means), if an active call is
663 // happening, we want to be in the dialer.
664 startLockedApp("dialer-app")
665 }
666 }
667 }
669 Connections {
670 id: powerConnection
671 target: Powerd
673 onStatusChanged: {
674 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
675 !callManager.hasCalls && !edgeDemo.running) {
676 // We don't want to simply call greeter.showNow() here, because
677 // that will take too long. Qt will delay button event
678 // handling until the greeter is done loading and may think the
679 // user held down the power button the whole time, leading to a
680 // power dialog being shown. Instead, delay showing the
681 // greeter until we've finished handling the event. We could
682 // make the greeter load asynchronously instead, but that
683 // introduces a whole host of timing issues, especially with
684 // its animations. So this is simpler.
685 showGreeterDelayed.start();
686 }
687 }
688 }
690 function showHome() {
691 if (edgeDemo.running) {
692 return
693 }
695 if (LightDM.Greeter.active) {
696 greeter.startUnlock()
697 }
699 var animate = !LightDM.Greeter.active && !stages.shown
700 dash.setCurrentScope(0, animate, false)
701 ApplicationManager.requestFocusApplication("unity8-dash")
702 }
704 function showDash() {
705 if (greeter.hasLockedApp || // just in case user gets here
706 (!greeter.narrowMode && shell.locked)) {
707 return
708 }
710 if (greeter.shown) {
711 greeter.hideRight();
712 launcher.fadeOut();
713 }
715 if (ApplicationManager.focusedApplicationId != "unity8-dash") {
716 ApplicationManager.requestFocusApplication("unity8-dash")
717 launcher.fadeOut();
718 }
719 }
721 Item {
722 id: overlay
723 z: 10
725 anchors.fill: parent
727 Panel {
728 id: panel
729 objectName: "panel"
730 anchors.fill: parent //because this draws indicator menus
731 indicators {
732 hides: [launcher]
733 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
734 contentEnabled: edgeDemo.panelContentEnabled
735 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
737 minimizedPanelHeight: units.gu(3)
738 expandedPanelHeight: units.gu(7)
740 indicatorsModel: visibleIndicators.model
741 }
743 VisibleIndicators {
744 id: visibleIndicators
745 // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
746 Component.onCompleted: initialise(indicatorProfile)
747 }
748 callHint {
749 greeterShown: greeter.shown || lockscreen.shown
750 }
752 property bool topmostApplicationIsFullscreen:
753 ApplicationManager.focusedApplicationId &&
754 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
756 fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
757 || greeter.hasLockedApp
758 }
760 Launcher {
761 id: launcher
762 objectName: "launcher"
764 readonly property bool dashSwipe: progress > 0
766 anchors.top: parent.top
767 anchors.topMargin: inverted ? 0 : panel.panelHeight
768 anchors.bottom: parent.bottom
769 width: parent.width
770 dragAreaWidth: shell.edgeSize
771 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
772 inverted: usageModeSettings.usageMode === "Staged"
774 onShowDashHome: showHome()
775 onDash: showDash()
776 onDashSwipeChanged: {
777 if (dashSwipe) {
778 dash.setCurrentScope(0, false, true)
779 }
780 }
781 onLauncherApplicationSelected: {
782 if (greeter.hasLockedApp) {
783 greeter.startUnlock()
784 }
785 if (!edgeDemo.running)
786 shell.activateApplication(appId)
787 }
788 onShownChanged: {
789 if (shown) {
790 panel.indicators.hide()
791 }
792 }
793 }
795 Wizard {
796 id: wizard
797 anchors.fill: parent
798 background: shell.background
799 }
801 Rectangle {
802 id: modalNotificationBackground
804 visible: notifications.useModal && (notifications.state == "narrow")
805 color: "#000000"
806 anchors.fill: parent
807 opacity: 0.9
809 MouseArea {
810 anchors.fill: parent
811 }
812 }
814 Notifications {
815 id: notifications
817 model: NotificationBackend.Model
818 margin: units.gu(1)
820 y: topmostIsFullscreen ? 0 : panel.panelHeight
821 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
823 states: [
824 State {
825 name: "narrow"
826 when: overlay.width <= units.gu(60)
827 AnchorChanges {
828 target: notifications
829 anchors.left: parent.left
830 anchors.right: parent.right
831 }
832 },
833 State {
834 name: "wide"
835 when: overlay.width > units.gu(60)
836 AnchorChanges {
837 target: notifications
838 anchors.left: undefined
839 anchors.right: parent.right
840 }
841 PropertyChanges { target: notifications; width: units.gu(38) }
842 }
843 ]
844 }
845 }
847 Dialogs {
848 id: dialogs
849 anchors.fill: parent
850 z: overlay.z + 10
851 onPowerOffClicked: {
852 shutdownFadeOutRectangle.enabled = true;
853 shutdownFadeOutRectangle.visible = true;
854 shutdownFadeOut.start();
855 }
856 }
858 EdgeDemo {
859 id: edgeDemo
860 objectName: "edgeDemo"
861 z: dialogs.z + 10
862 paused: Powerd.status === Powerd.Off || wizard.active // Saves power
863 greeter: greeter
864 launcher: launcher
865 panel: panel
866 stages: stages
867 }
869 Connections {
870 target: SessionBroadcast
871 onShowHome: showHome()
872 }
874 Rectangle {
875 id: shutdownFadeOutRectangle
876 z: edgeDemo.z + 10
877 enabled: false
878 visible: false
879 color: "black"
880 anchors.fill: parent
881 opacity: 0.0
882 NumberAnimation on opacity {
883 id: shutdownFadeOut
884 from: 0.0
885 to: 1.0
886 onStopped: {
887 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
888 DBusUnitySessionService.Shutdown();
889 }
890 }
891 }
892 }