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