on 81dc450 help sign in
unity8/qml/Shell.qml
870 lines 31 kB history raw
1/*
2 * Copyright (C) 2013-2016 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.4
18import QtQuick.Window 2.2
19import AccountsService 0.1
20import Unity.Application 0.1
21import Ubuntu.Components 1.3
22import Ubuntu.Components.Popups 1.3
23import Ubuntu.Gestures 0.1
24import Ubuntu.Telephony 0.1 as Telephony
25import Unity.Connectivity 0.1
26import Unity.Launcher 0.1
27import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
28import GSettings 1.0
29import ImageCache 0.1
30import Utils 0.1
31import Powerd 0.1
32import SessionBroadcast 0.1
33import "Greeter"
34import "Launcher"
35import "Panel"
36import "Components"
37import "Notifications"
38import "Stages"
39import "Tutorial"
40import "Wizard"
41import Unity.Notifications 1.0 as NotificationBackend
42import Unity.Session 0.1
43import Unity.DashCommunicator 0.1
44import Unity.Indicators 0.1 as Indicators
45import Cursor 1.1
46import WindowManager 0.1
49StyledItem {
50 id: shell
52 theme.name: "Ubuntu.Components.Themes.SuruDark"
54 // to be set from outside
55 property int orientationAngle: 0
56 property int orientation
57 property Orientations orientations
58 property real nativeWidth
59 property real nativeHeight
60 property alias indicatorAreaShowProgress: panel.indicatorAreaShowProgress
61 property bool beingResized
62 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
63 property string mode: "full-greeter"
64 property alias oskEnabled: inputMethod.enabled
65 function updateFocusedAppOrientation() {
66 applicationsDisplayLoader.item.updateFocusedAppOrientation();
67 }
68 function updateFocusedAppOrientationAnimated() {
69 applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
70 }
71 property bool hasMouse: false
73 // to be read from outside
74 readonly property int mainAppWindowOrientationAngle:
75 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindowOrientationAngle : 0
77 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
78 && (applicationsDisplayLoader.item && applicationsDisplayLoader.item.orientationChangesEnabled)
79 && (!greeter || !greeter.animating)
81 readonly property bool showingGreeter: greeter && greeter.shown
83 property bool startingUp: true
84 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
86 property int supportedOrientations: {
87 if (startingUp) {
88 // Ensure we don't rotate during start up
89 return Qt.PrimaryOrientation;
90 } else if (showingGreeter || notifications.topmostIsFullscreen) {
91 return Qt.PrimaryOrientation;
92 } else if (applicationsDisplayLoader.item) {
93 return shell.orientations.map(applicationsDisplayLoader.item.supportedOrientations);
94 } else {
95 // we just don't care
96 return Qt.PortraitOrientation
97 | Qt.LandscapeOrientation
98 | Qt.InvertedPortraitOrientation
99 | Qt.InvertedLandscapeOrientation;
100 }
101 }
103 readonly property var mainApp:
104 applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null
105 onMainAppChanged: {
106 if (mainApp) {
107 _onMainAppChanged(mainApp.appId);
108 }
109 }
110 Connections {
111 target: ApplicationManager
112 onFocusRequested: {
113 if (shell.mainApp && shell.mainApp.appId === appId) {
114 _onMainAppChanged(appId);
115 }
116 }
117 }
118 function _onMainAppChanged(appId) {
119 if (wizard.active && appId != "" && appId != "unity8-dash") {
120 // If this happens on first boot, we may be in the
121 // wizard while receiving a call. But a call is more
122 // important than the wizard so just bail out of it.
123 wizard.hide();
124 }
126 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
127 // If we are in the middle of a call, make dialer lockedApp and show it.
128 // This can happen if user backs out of dialer back to greeter, then
129 // launches dialer again.
130 greeter.lockedApp = appId;
131 }
132 greeter.notifyAppFocusRequested(appId);
134 panel.indicators.hide();
135 launcher.hide();
136 }
138 // For autopilot consumption
139 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
141 // Note when greeter is waiting on PAM, so that we can disable edges until
142 // we know which user data to show and whether the session is locked.
143 readonly property bool waitingOnGreeter: greeter && greeter.waiting
145 property real edgeSize: units.gu(settings.edgeDragWidth)
147 WallpaperResolver {
148 id: wallpaperResolver
149 objectName: "wallpaperResolver"
151 readonly property url defaultBackground: "file:///usr/share/backgrounds/warty-final-ubuntu.png"
152 readonly property bool hasCustomBackground: background != defaultBackground
154 // Use a cached version of the scaled-down wallpaper (as sometimes the
155 // image can be quite big compared to the device size, including for
156 // our default wallpaper). We use a name=wallpaper argument here to
157 // make sure we don't litter our cache with lots of scaled images. We
158 // only need to bother caching one at a time.
159 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
161 GSettings {
162 id: backgroundSettings
163 schema.id: "org.gnome.desktop.background"
164 }
166 candidates: [
167 AccountsService.backgroundFile,
168 backgroundSettings.pictureUri,
169 defaultBackground
170 ]
171 }
173 readonly property alias greeter: greeterLoader.item
175 function activateApplication(appId) {
176 // Either open the app in our own session, or -- if we're acting as a
177 // greeter -- ask the user's session to open it for us.
178 if (shell.mode === "greeter") {
179 activateURL("application:///" + appId + ".desktop");
180 } else {
181 startApp(appId);
182 }
183 }
185 function activateURL(url) {
186 SessionBroadcast.requestUrlStart(AccountsService.user, url);
187 greeter.notifyUserRequestedApp();
188 panel.indicators.hide();
189 }
191 function startApp(appId) {
192 if (ApplicationManager.findApplication(appId)) {
193 ApplicationManager.requestFocusApplication(appId);
194 } else {
195 ApplicationManager.startApplication(appId);
196 }
197 }
199 function startLockedApp(app) {
200 if (greeter.locked) {
201 greeter.lockedApp = app;
202 }
203 startApp(app); // locked apps are always in our same session
204 }
206 Binding {
207 target: LauncherModel
208 property: "applicationManager"
209 value: ApplicationManager
210 }
212 Component.onCompleted: {
213 finishStartUpTimer.start();
214 }
216 VolumeControl {
217 id: volumeControl
218 indicators: panel.indicators
219 }
221 DashCommunicator {
222 id: dash
223 objectName: "dashCommunicator"
224 }
226 PhysicalKeysMapper {
227 id: physicalKeysMapper
228 objectName: "physicalKeysMapper"
230 onPowerKeyLongPressed: dialogs.showPowerDialog();
231 onVolumeDownTriggered: volumeControl.volumeDown();
232 onVolumeUpTriggered: volumeControl.volumeUp();
233 onScreenshotTriggered: itemGrabber.capture(shell);
234 }
236 GlobalShortcut {
237 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
238 }
240 WindowInputFilter {
241 id: inputFilter
242 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
243 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
244 }
246 WindowInputMonitor {
247 objectName: "windowInputMonitor"
248 onHomeKeyActivated: {
249 // Ignore when greeter is active, to avoid pocket presses
250 if (!greeter.active) {
251 launcher.fadeOut();
252 shell.showHome();
253 }
254 }
255 onTouchBegun: { cursor.opacity = 0; }
256 onTouchEnded: {
257 // move the (hidden) cursor to the last known touch position
258 var mappedCoords = mapFromItem(null, pos.x, pos.y);
259 cursor.x = mappedCoords.x;
260 cursor.y = mappedCoords.y;
261 cursor.mouseNeverMoved = false;
262 }
263 }
265 GSettings {
266 id: settings
267 schema.id: "com.canonical.Unity8"
268 }
270 Item {
271 id: stages
272 objectName: "stages"
273 width: parent.width
274 height: parent.height
276 TopLevelSurfaceList {
277 id: topLevelSurfaceList
278 objectName: "topLevelSurfaceList"
279 applicationsModel: ApplicationManager
280 }
282 Loader {
283 id: applicationsDisplayLoader
284 objectName: "applicationsDisplayLoader"
285 anchors.fill: parent
287 // When we have a locked app, we only want to show that one app.
288 // FIXME: do this in a less traumatic way. We currently only allow
289 // locked apps in phone mode (see FIXME in Lockscreen component in
290 // this same file). When that changes, we need to do something
291 // nicer here. But this code is currently just to prevent a
292 // theoretical attack where user enters lockedApp mode, then makes
293 // the screen larger (maybe connects to monitor) and tries to enter
294 // tablet mode.
296 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
297 ? "phone"
298 : shell.usageScenario
299 readonly property string qmlComponent: {
300 if (applicationsDisplayLoader.usageScenario === "phone") {
301 return "Stages/PhoneStage.qml";
302 } else if (applicationsDisplayLoader.usageScenario === "tablet") {
303 return "Stages/TabletStage.qml";
304 } else {
305 return "Stages/DesktopStage.qml";
306 }
307 }
308 // TODO: Ensure the current stage is destroyed before the new one gets loaded.
309 // Currently the new one will get loaded while the old is still hanging
310 // around for a bit, which might lead to conflicts where both stages
311 // change the model simultaneously.
312 onQmlComponentChanged: {
313 if (item) item.stageAboutToBeUnloaded();
314 source = qmlComponent;
315 }
317 property bool interactive: (!greeter || !greeter.shown)
318 && panel.indicators.fullyClosed
319 && launcher.progress == 0
320 && !notifications.useModal
322 onInteractiveChanged: { if (interactive) { focus = true; } }
324 Binding {
325 target: applicationsDisplayLoader.item
326 property: "focus"
327 value: true
328 }
329 Binding {
330 target: applicationsDisplayLoader.item
331 property: "objectName"
332 value: "stage"
333 }
334 Binding {
335 target: applicationsDisplayLoader.item
336 property: "dragAreaWidth"
337 value: shell.edgeSize
338 }
339 Binding {
340 target: applicationsDisplayLoader.item
341 property: "maximizedAppTopMargin"
342 // Not just using panel.panelHeight as that changes depending on the focused app.
343 value: panel.indicators.minimizedPanelHeight
344 }
345 Binding {
346 target: applicationsDisplayLoader.item
347 property: "interactive"
348 value: applicationsDisplayLoader.interactive
349 }
350 Binding {
351 target: applicationsDisplayLoader.item
352 property: "spreadEnabled"
353 value: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
354 }
355 Binding {
356 target: applicationsDisplayLoader.item
357 property: "inverseProgress"
358 value: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 : launcher.progress
359 }
360 Binding {
361 target: applicationsDisplayLoader.item
362 property: "shellOrientationAngle"
363 value: shell.orientationAngle
364 }
365 Binding {
366 target: applicationsDisplayLoader.item
367 property: "shellOrientation"
368 value: shell.orientation
369 }
370 Binding {
371 target: applicationsDisplayLoader.item
372 property: "orientations"
373 value: shell.orientations
374 }
375 Binding {
376 target: applicationsDisplayLoader.item
377 property: "background"
378 value: wallpaperResolver.cachedBackground
379 }
380 Binding {
381 target: applicationsDisplayLoader.item
382 property: "nativeWidth"
383 value: shell.nativeWidth
384 }
385 Binding {
386 target: applicationsDisplayLoader.item
387 property: "nativeHeight"
388 value: shell.nativeHeight
389 }
390 Binding {
391 target: applicationsDisplayLoader.item
392 property: "beingResized"
393 value: shell.beingResized
394 }
395 Binding {
396 target: applicationsDisplayLoader.item
397 property: "keepDashRunning"
398 value: launcher.shown || launcher.dashSwipe
399 }
400 Binding {
401 target: applicationsDisplayLoader.item
402 property: "suspended"
403 value: greeter.shown
404 }
405 Binding {
406 target: applicationsDisplayLoader.item
407 property: "altTabPressed"
408 value: physicalKeysMapper.altTabPressed
409 }
410 Binding {
411 target: applicationsDisplayLoader.item
412 property: "leftMargin"
413 value: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
414 }
415 Binding {
416 target: applicationsDisplayLoader.item
417 property: "applicationManager"
418 value: ApplicationManager
419 }
420 Binding {
421 target: applicationsDisplayLoader.item
422 property: "topLevelSurfaceList"
423 value: topLevelSurfaceList
424 }
425 Binding {
426 target: applicationsDisplayLoader.item
427 property: "oskEnabled"
428 value: shell.oskEnabled
429 }
430 }
431 }
433 InputMethod {
434 id: inputMethod
435 objectName: "inputMethod"
436 anchors {
437 fill: parent
438 topMargin: panel.panelHeight
439 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
440 }
441 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
442 }
444 Loader {
445 id: greeterLoader
446 anchors.fill: parent
447 anchors.topMargin: panel.panelHeight
448 sourceComponent: shell.mode != "shell" ? integratedGreeter :
449 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
450 onLoaded: {
451 item.objectName = "greeter"
452 }
453 }
455 Component {
456 id: integratedGreeter
457 Greeter {
459 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
460 hides: [launcher, panel.indicators]
461 tabletMode: shell.usageScenario != "phone"
462 launcherOffset: launcher.progress
463 forcedUnlock: wizard.active || shell.mode === "full-shell"
464 background: wallpaperResolver.cachedBackground
465 hasCustomBackground: wallpaperResolver.hasCustomBackground
466 allowFingerprint: !dialogs.hasActiveDialog &&
467 !notifications.topmostIsFullscreen &&
468 !panel.indicators.shown
470 // avoid overlapping with Launcher's edge drag area
471 // FIXME: Fix TouchRegistry & friends and remove this workaround
472 // Issue involves launcher's DDA getting disabled on a long
473 // left-edge drag
474 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
476 onSessionStarted: {
477 launcher.hide();
478 }
480 onTease: {
481 if (!tutorial.running) {
482 launcher.tease();
483 }
484 }
486 onEmergencyCall: startLockedApp("dialer-app")
487 }
488 }
490 Timer {
491 // See powerConnection for why this is useful
492 id: showGreeterDelayed
493 interval: 1
494 onTriggered: {
495 greeter.forceShow();
496 }
497 }
499 Connections {
500 id: callConnection
501 target: callManager
503 onHasCallsChanged: {
504 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
505 // We just received an incoming call while locked. The
506 // indicator will have already launched dialer-app for us, but
507 // there is a race between "hasCalls" changing and the dialer
508 // starting up. So in case we lose that race, we'll start/
509 // focus the dialer ourselves here too. Even if the indicator
510 // didn't launch the dialer for some reason (or maybe a call
511 // started via some other means), if an active call is
512 // happening, we want to be in the dialer.
513 startLockedApp("dialer-app")
514 }
515 }
516 }
518 Connections {
519 id: powerConnection
520 target: Powerd
522 onStatusChanged: {
523 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
524 !callManager.hasCalls && !wizard.active) {
525 // We don't want to simply call greeter.showNow() here, because
526 // that will take too long. Qt will delay button event
527 // handling until the greeter is done loading and may think the
528 // user held down the power button the whole time, leading to a
529 // power dialog being shown. Instead, delay showing the
530 // greeter until we've finished handling the event. We could
531 // make the greeter load asynchronously instead, but that
532 // introduces a whole host of timing issues, especially with
533 // its animations. So this is simpler.
534 showGreeterDelayed.start();
535 }
536 }
537 }
539 function showHome() {
540 greeter.notifyUserRequestedApp();
542 if (shell.mode === "greeter") {
543 SessionBroadcast.requestHomeShown(AccountsService.user);
544 } else {
545 var animate = !LightDMService.greeter.active && !stages.shown;
546 dash.setCurrentScope(0, animate, false);
547 ApplicationManager.requestFocusApplication("unity8-dash");
548 }
549 }
551 function showDash() {
552 if (greeter.notifyShowingDashFromDrag()) {
553 launcher.fadeOut();
554 }
556 if (!greeter.locked && tutorial.launcherLongSwipeEnabled
557 && ApplicationManager.focusedApplicationId != "unity8-dash") {
558 ApplicationManager.requestFocusApplication("unity8-dash")
559 launcher.fadeOut();
560 }
561 }
563 Item {
564 id: overlay
565 z: 10
567 anchors.fill: parent
569 Panel {
570 id: panel
571 objectName: "panel"
572 anchors.fill: parent //because this draws indicator menus
573 indicators {
574 hides: [launcher]
575 available: tutorial.panelEnabled
576 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
577 && (!greeter || !greeter.hasLockedApp)
578 && !shell.waitingOnGreeter
579 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
581 minimizedPanelHeight: units.gu(3)
582 expandedPanelHeight: units.gu(7)
584 indicatorsModel: Indicators.IndicatorsModel {
585 // tablet and phone both use the same profile
586 // FIXME: use just "phone" for greeter too, but first fix
587 // greeter app launching to either load the app inside the
588 // greeter or tell the session to load the app. This will
589 // involve taking the url-dispatcher dbus name and using
590 // SessionBroadcast to tell the session.
591 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
592 Component.onCompleted: load();
593 }
594 }
596 callHint {
597 greeterShown: greeter.shown
598 }
600 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
601 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
602 : false
603 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
604 || greeter.hasLockedApp
605 locked: greeter && greeter.active
606 }
608 Launcher {
609 id: launcher
610 objectName: "launcher"
612 /*
613 * Since the Dash doesn't have the same controll over surfaces that the
614 * Shell does, it can't slowly move the scope out of the way, as the shell
615 * does with apps, and the dash is show instantly. This allows for some
616 * leeway and prevents accidental home swipes.
617 */
618 readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
619 readonly property bool dashSwipe: progress > offset
621 anchors.top: parent.top
622 anchors.topMargin: inverted ? 0 : panel.panelHeight
623 anchors.bottom: parent.bottom
624 width: parent.width
625 dragAreaWidth: shell.edgeSize
626 available: tutorial.launcherEnabled
627 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
628 && !greeter.hasLockedApp
629 && !shell.waitingOnGreeter
630 inverted: shell.usageScenario !== "desktop"
631 superPressed: physicalKeysMapper.superPressed
632 superTabPressed: physicalKeysMapper.superTabPressed
633 panelWidth: units.gu(settings.launcherWidth)
634 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
636 onShowDashHome: showHome()
637 onDash: showDash()
638 onDashSwipeChanged: {
639 if (dashSwipe) {
640 dash.setCurrentScope(0, false, true)
641 }
642 }
643 onLauncherApplicationSelected: {
644 greeter.notifyUserRequestedApp();
645 shell.activateApplication(appId);
646 }
647 onShownChanged: {
648 if (shown) {
649 panel.indicators.hide()
650 }
651 }
652 onFocusChanged: {
653 if (!focus) {
654 applicationsDisplayLoader.focus = true;
655 }
656 }
658 GlobalShortcut {
659 shortcut: Qt.AltModifier | Qt.Key_F1
660 onTriggered: {
661 launcher.openForKeyboardNavigation();
662 }
663 }
664 GlobalShortcut {
665 shortcut: Qt.MetaModifier | Qt.Key_0
666 onTriggered: {
667 if (LauncherModel.get(9)) {
668 activateApplication(LauncherModel.get(9).appId);
669 }
670 }
671 }
672 Repeater {
673 model: 9
674 GlobalShortcut {
675 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
676 onTriggered: {
677 if (LauncherModel.get(index)) {
678 activateApplication(LauncherModel.get(index).appId);
679 }
680 }
681 }
682 }
683 }
685 KeyboardShortcutsOverlay {
686 objectName: "shortcutsOverlay"
687 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
688 && height < parent.height - padding - panel.panelHeight
689 anchors.centerIn: parent
690 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
691 anchors.verticalCenterOffset: panel.panelHeight/2
692 visible: opacity > 0
693 opacity: enabled ? 0.95 : 0
695 Behavior on opacity {
696 UbuntuNumberAnimation {}
697 }
698 }
700 Tutorial {
701 id: tutorial
702 objectName: "tutorial"
703 anchors.fill: parent
705 paused: callManager.hasCalls || !greeter || greeter.active ||
706 wizard.active
707 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
708 inputMethod.visible ||
709 (launcher.shown && !launcher.lockedVisible) ||
710 panel.indicators.shown || stage.dragProgress > 0
711 usageScenario: shell.usageScenario
712 lastInputTimestamp: inputFilter.lastInputTimestamp
713 launcher: launcher
714 panel: panel
715 stage: applicationsDisplayLoader.item
716 }
718 Wizard {
719 id: wizard
720 objectName: "wizard"
721 anchors.fill: parent
722 deferred: shell.mode === "greeter"
724 function unlockWhenDoneWithWizard() {
725 if (!active) {
726 Connectivity.unlockAllModems();
727 }
728 }
730 Component.onCompleted: unlockWhenDoneWithWizard()
731 onActiveChanged: unlockWhenDoneWithWizard()
732 }
734 MouseArea { // modal notifications prevent interacting with other contents
735 anchors.fill: parent
736 visible: notifications.useModal
737 enabled: visible
738 }
740 Notifications {
741 id: notifications
743 model: NotificationBackend.Model
744 margin: units.gu(1)
745 hasMouse: shell.hasMouse
746 background: wallpaperResolver.cachedBackground
748 y: topmostIsFullscreen ? 0 : panel.panelHeight
749 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
751 states: [
752 State {
753 name: "narrow"
754 when: overlay.width <= units.gu(60)
755 AnchorChanges {
756 target: notifications
757 anchors.left: parent.left
758 anchors.right: parent.right
759 }
760 },
761 State {
762 name: "wide"
763 when: overlay.width > units.gu(60)
764 AnchorChanges {
765 target: notifications
766 anchors.left: undefined
767 anchors.right: parent.right
768 }
769 PropertyChanges { target: notifications; width: units.gu(38) }
770 }
771 ]
772 }
773 }
775 Dialogs {
776 id: dialogs
777 objectName: "dialogs"
778 anchors.fill: parent
779 z: overlay.z + 10
780 usageScenario: shell.usageScenario
781 onPowerOffClicked: {
782 shutdownFadeOutRectangle.enabled = true;
783 shutdownFadeOutRectangle.visible = true;
784 shutdownFadeOut.start();
785 }
786 }
788 Connections {
789 target: SessionBroadcast
790 onShowHome: if (shell.mode !== "greeter") showHome()
791 }
793 URLDispatcher {
794 id: urlDispatcher
795 objectName: "urlDispatcher"
796 active: shell.mode === "greeter"
797 onUrlRequested: shell.activateURL(url)
798 }
800 ItemGrabber {
801 id: itemGrabber
802 anchors.fill: parent
803 z: dialogs.z + 10
804 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
805 Connections {
806 target: applicationsDisplayLoader.item
807 ignoreUnknownSignals: true
808 onItemSnapshotRequested: itemGrabber.capture(item)
809 }
810 }
812 Cursor {
813 id: cursor
814 visible: shell.hasMouse
815 z: itemGrabber.z + 1
817 property bool mouseNeverMoved: true
818 Binding {
819 target: cursor; property: "x"; value: shell.width / 2
820 when: cursor.mouseNeverMoved && cursor.visible
821 }
822 Binding {
823 target: cursor; property: "y"; value: shell.height / 2
824 when: cursor.mouseNeverMoved && cursor.visible
825 }
827 height: units.gu(3)
829 onPushedLeftBoundary: {
830 if (buttons === Qt.NoButton) {
831 launcher.pushEdge(amount);
832 }
833 }
835 onPushedRightBoundary: {
836 if (buttons === Qt.NoButton && applicationsDisplayLoader.item
837 && applicationsDisplayLoader.item.pushRightEdge) {
838 applicationsDisplayLoader.item.pushRightEdge(amount);
839 }
840 }
842 onMouseMoved: {
843 mouseNeverMoved = false;
844 cursor.opacity = 1;
845 }
846 }
848 // non-visual object
849 KeymapSwitcher {}
851 Rectangle {
852 id: shutdownFadeOutRectangle
853 z: cursor.z + 1
854 enabled: false
855 visible: false
856 color: "black"
857 anchors.fill: parent
858 opacity: 0.0
859 NumberAnimation on opacity {
860 id: shutdownFadeOut
861 from: 0.0
862 to: 1.0
863 onStopped: {
864 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
865 DBusUnitySessionService.shutdown();
866 }
867 }
868 }
869 }