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