on c2efb1d help sign in
unity8/qml/Shell.qml
820 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 }
182 DashCommunicator {
183 id: dash
184 objectName: "dashCommunicator"
185 }
187 PhysicalKeysMapper {
188 id: physicalKeysMapper
189 objectName: "physicalKeysMapper"
191 onPowerKeyLongPressed: dialogs.showPowerDialog();
192 onVolumeDownTriggered: volumeControl.volumeDown();
193 onVolumeUpTriggered: volumeControl.volumeUp();
194 onScreenshotTriggered: itemGrabber.capture(shell);
195 }
197 GlobalShortcut {
198 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
199 }
201 WindowInputFilter {
202 id: inputFilter
203 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
204 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
205 }
207 WindowInputMonitor {
208 objectName: "windowInputMonitor"
209 onHomeKeyActivated: {
210 // Ignore when greeter is active, to avoid pocket presses
211 if (!greeter.active) {
212 launcher.fadeOut();
213 shell.showHome();
214 }
215 }
216 onTouchBegun: { cursor.opacity = 0; }
217 onTouchEnded: {
218 // move the (hidden) cursor to the last known touch position
219 var mappedCoords = mapFromItem(null, pos.x, pos.y);
220 cursor.x = mappedCoords.x;
221 cursor.y = mappedCoords.y;
222 cursor.mouseNeverMoved = false;
223 }
224 }
226 GSettings {
227 id: settings
228 schema.id: "com.canonical.Unity8"
229 }
231 Item {
232 id: stages
233 objectName: "stages"
234 width: parent.width
235 height: parent.height
237 TopLevelSurfaceList {
238 id: topLevelSurfaceList
239 objectName: "topLevelSurfaceList"
240 applicationsModel: ApplicationManager
241 }
243 Loader {
244 id: applicationsDisplayLoader
245 objectName: "applicationsDisplayLoader"
246 anchors.fill: parent
248 // When we have a locked app, we only want to show that one app.
249 // FIXME: do this in a less traumatic way. We currently only allow
250 // locked apps in phone mode (see FIXME in Lockscreen component in
251 // this same file). When that changes, we need to do something
252 // nicer here. But this code is currently just to prevent a
253 // theoretical attack where user enters lockedApp mode, then makes
254 // the screen larger (maybe connects to monitor) and tries to enter
255 // tablet mode.
257 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
258 ? "phone"
259 : shell.usageScenario
260 readonly property string qmlComponent: {
261 if (shell.mode === "greeter") {
262 return "Stages/AbstractStage.qml"
263 } else if (applicationsDisplayLoader.usageScenario === "phone") {
264 return "Stages/PhoneStage.qml";
265 } else if (applicationsDisplayLoader.usageScenario === "tablet") {
266 return "Stages/TabletStage.qml";
267 } else {
268 return "Stages/DesktopStage.qml";
269 }
270 }
271 // TODO: Ensure the current stage is destroyed before the new one gets loaded.
272 // Currently the new one will get loaded while the old is still hanging
273 // around for a bit, which might lead to conflicts where both stages
274 // change the model simultaneously.
275 onQmlComponentChanged: {
276 if (item) item.stageAboutToBeUnloaded();
277 source = qmlComponent;
278 }
280 property bool interactive: (!greeter || !greeter.shown)
281 && panel.indicators.fullyClosed
282 && launcher.progress == 0
283 && !notifications.useModal
285 onInteractiveChanged: { if (interactive) { focus = true; } }
287 Binding {
288 target: applicationsDisplayLoader.item
289 property: "focus"
290 value: true
291 }
292 Binding {
293 target: applicationsDisplayLoader.item
294 property: "objectName"
295 value: "stage"
296 }
297 Binding {
298 target: applicationsDisplayLoader.item
299 property: "dragAreaWidth"
300 value: shell.edgeSize
301 }
302 Binding {
303 target: applicationsDisplayLoader.item
304 property: "maximizedAppTopMargin"
305 // Not just using panel.panelHeight as that changes depending on the focused app.
306 value: panel.indicators.minimizedPanelHeight
307 }
308 Binding {
309 target: applicationsDisplayLoader.item
310 property: "interactive"
311 value: applicationsDisplayLoader.interactive
312 }
313 Binding {
314 target: applicationsDisplayLoader.item
315 property: "spreadEnabled"
316 value: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
317 }
318 Binding {
319 target: applicationsDisplayLoader.item
320 property: "inverseProgress"
321 value: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 : launcher.progress
322 }
323 Binding {
324 target: applicationsDisplayLoader.item
325 property: "shellOrientationAngle"
326 value: shell.orientationAngle
327 }
328 Binding {
329 target: applicationsDisplayLoader.item
330 property: "shellOrientation"
331 value: shell.orientation
332 }
333 Binding {
334 target: applicationsDisplayLoader.item
335 property: "orientations"
336 value: shell.orientations
337 }
338 Binding {
339 target: applicationsDisplayLoader.item
340 property: "background"
341 value: wallpaperResolver.background
342 }
343 Binding {
344 target: applicationsDisplayLoader.item
345 property: "nativeWidth"
346 value: shell.nativeWidth
347 }
348 Binding {
349 target: applicationsDisplayLoader.item
350 property: "nativeHeight"
351 value: shell.nativeHeight
352 }
353 Binding {
354 target: applicationsDisplayLoader.item
355 property: "beingResized"
356 value: shell.beingResized
357 }
358 Binding {
359 target: applicationsDisplayLoader.item
360 property: "keepDashRunning"
361 value: launcher.shown || launcher.dashSwipe
362 }
363 Binding {
364 target: applicationsDisplayLoader.item
365 property: "suspended"
366 value: greeter.shown
367 }
368 Binding {
369 target: applicationsDisplayLoader.item
370 property: "altTabPressed"
371 value: physicalKeysMapper.altTabPressed
372 }
373 Binding {
374 target: applicationsDisplayLoader.item
375 property: "leftMargin"
376 value: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
377 }
378 Binding {
379 target: applicationsDisplayLoader.item
380 property: "applicationManager"
381 value: ApplicationManager
382 }
383 Binding {
384 target: applicationsDisplayLoader.item
385 property: "topLevelSurfaceList"
386 value: topLevelSurfaceList
387 }
388 Binding {
389 target: applicationsDisplayLoader.item
390 property: "oskEnabled"
391 value: shell.oskEnabled
392 }
393 }
394 }
396 InputMethod {
397 id: inputMethod
398 objectName: "inputMethod"
399 anchors {
400 fill: parent
401 topMargin: panel.panelHeight
402 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
403 }
404 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
405 }
407 Loader {
408 id: greeterLoader
409 anchors.fill: parent
410 anchors.topMargin: panel.panelHeight
411 sourceComponent: shell.mode != "shell" ? integratedGreeter :
412 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
413 onLoaded: {
414 item.objectName = "greeter"
415 }
416 }
418 Component {
419 id: integratedGreeter
420 Greeter {
422 hides: [launcher, panel.indicators]
423 tabletMode: shell.usageScenario != "phone"
424 launcherOffset: launcher.progress
425 forcedUnlock: wizard.active || shell.mode === "full-shell"
426 background: wallpaperResolver.background
427 allowFingerprint: !dialogs.hasActiveDialog &&
428 !notifications.topmostIsFullscreen &&
429 !panel.indicators.shown
431 // avoid overlapping with Launcher's edge drag area
432 // FIXME: Fix TouchRegistry & friends and remove this workaround
433 // Issue involves launcher's DDA getting disabled on a long
434 // left-edge drag
435 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
437 onSessionStarted: {
438 launcher.hide();
439 }
441 onTease: {
442 if (!tutorial.running) {
443 launcher.tease();
444 }
445 }
447 onEmergencyCall: startLockedApp("dialer-app")
448 }
449 }
451 Timer {
452 // See powerConnection for why this is useful
453 id: showGreeterDelayed
454 interval: 1
455 onTriggered: {
456 greeter.forceShow();
457 }
458 }
460 Connections {
461 id: callConnection
462 target: callManager
464 onHasCallsChanged: {
465 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
466 // We just received an incoming call while locked. The
467 // indicator will have already launched dialer-app for us, but
468 // there is a race between "hasCalls" changing and the dialer
469 // starting up. So in case we lose that race, we'll start/
470 // focus the dialer ourselves here too. Even if the indicator
471 // didn't launch the dialer for some reason (or maybe a call
472 // started via some other means), if an active call is
473 // happening, we want to be in the dialer.
474 startLockedApp("dialer-app")
475 }
476 }
477 }
479 Connections {
480 id: powerConnection
481 target: Powerd
483 onStatusChanged: {
484 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
485 !callManager.hasCalls && !wizard.active) {
486 // We don't want to simply call greeter.showNow() here, because
487 // that will take too long. Qt will delay button event
488 // handling until the greeter is done loading and may think the
489 // user held down the power button the whole time, leading to a
490 // power dialog being shown. Instead, delay showing the
491 // greeter until we've finished handling the event. We could
492 // make the greeter load asynchronously instead, but that
493 // introduces a whole host of timing issues, especially with
494 // its animations. So this is simpler.
495 showGreeterDelayed.start();
496 }
497 }
498 }
500 function showHome() {
501 greeter.notifyUserRequestedApp("unity8-dash");
503 var animate = !LightDMService.greeter.active && !stages.shown
504 dash.setCurrentScope(0, animate, false)
505 ApplicationManager.requestFocusApplication("unity8-dash")
506 }
508 function showDash() {
509 if (greeter.notifyShowingDashFromDrag()) {
510 launcher.fadeOut();
511 }
513 if (!greeter.locked && tutorial.launcherLongSwipeEnabled
514 && ApplicationManager.focusedApplicationId != "unity8-dash") {
515 ApplicationManager.requestFocusApplication("unity8-dash")
516 launcher.fadeOut();
517 }
518 }
520 Item {
521 id: overlay
522 z: 10
524 anchors.fill: parent
526 Panel {
527 id: panel
528 objectName: "panel"
529 anchors.fill: parent //because this draws indicator menus
530 indicators {
531 hides: [launcher]
532 available: tutorial.panelEnabled
533 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
534 && (!greeter || !greeter.hasLockedApp)
535 && !shell.waitingOnGreeter
536 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
538 minimizedPanelHeight: units.gu(3)
539 expandedPanelHeight: units.gu(7)
541 indicatorsModel: Indicators.IndicatorsModel {
542 // tablet and phone both use the same profile
543 // FIXME: use just "phone" for greeter too, but first fix
544 // greeter app launching to either load the app inside the
545 // greeter or tell the session to load the app. This will
546 // involve taking the url-dispatcher dbus name and using
547 // SessionBroadcast to tell the session.
548 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
549 Component.onCompleted: load();
550 }
551 }
553 callHint {
554 greeterShown: greeter.shown
555 }
557 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
558 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
559 : false
560 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
561 || greeter.hasLockedApp
562 locked: greeter && greeter.active
563 }
565 Launcher {
566 id: launcher
567 objectName: "launcher"
569 /*
570 * Since the Dash doesn't have the same controll over surfaces that the
571 * Shell does, it can't slowly move the scope out of the way, as the shell
572 * does with apps, and the dash is show instantly. This allows for some
573 * leeway and prevents accidental home swipes.
574 */
575 readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
576 readonly property bool dashSwipe: progress > offset
578 anchors.top: parent.top
579 anchors.topMargin: inverted ? 0 : panel.panelHeight
580 anchors.bottom: parent.bottom
581 width: parent.width
582 dragAreaWidth: shell.edgeSize
583 available: tutorial.launcherEnabled
584 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
585 && !greeter.hasLockedApp
586 && !shell.waitingOnGreeter
587 inverted: shell.usageScenario !== "desktop"
588 superPressed: physicalKeysMapper.superPressed
589 superTabPressed: physicalKeysMapper.superTabPressed
590 panelWidth: units.gu(settings.launcherWidth)
591 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
593 onShowDashHome: showHome()
594 onDash: showDash()
595 onDashSwipeChanged: {
596 if (dashSwipe) {
597 dash.setCurrentScope(0, false, true)
598 }
599 }
600 onLauncherApplicationSelected: {
601 greeter.notifyUserRequestedApp(appId);
602 shell.activateApplication(appId);
603 }
604 onShownChanged: {
605 if (shown) {
606 panel.indicators.hide()
607 }
608 }
609 onFocusChanged: {
610 if (!focus) {
611 applicationsDisplayLoader.focus = true;
612 }
613 }
615 GlobalShortcut {
616 shortcut: Qt.AltModifier | Qt.Key_F1
617 onTriggered: {
618 launcher.openForKeyboardNavigation();
619 }
620 }
621 GlobalShortcut {
622 shortcut: Qt.MetaModifier | Qt.Key_0
623 onTriggered: {
624 if (LauncherModel.get(9)) {
625 activateApplication(LauncherModel.get(9).appId);
626 }
627 }
628 }
629 Repeater {
630 model: 9
631 GlobalShortcut {
632 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
633 onTriggered: {
634 if (LauncherModel.get(index)) {
635 activateApplication(LauncherModel.get(index).appId);
636 }
637 }
638 }
639 }
640 }
642 KeyboardShortcutsOverlay {
643 objectName: "shortcutsOverlay"
644 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
645 && height < parent.height - padding - panel.panelHeight
646 anchors.centerIn: parent
647 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
648 anchors.verticalCenterOffset: panel.panelHeight/2
649 visible: opacity > 0
650 opacity: enabled ? 0.95 : 0
652 Behavior on opacity {
653 UbuntuNumberAnimation {}
654 }
655 }
657 Tutorial {
658 id: tutorial
659 objectName: "tutorial"
660 anchors.fill: parent
662 paused: callManager.hasCalls || !greeter || greeter.shown ||
663 wizard.active
664 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
665 inputMethod.visible ||
666 (launcher.shown && !launcher.lockedVisible) ||
667 panel.indicators.shown || stage.dragProgress > 0
668 usageScenario: shell.usageScenario
669 lastInputTimestamp: inputFilter.lastInputTimestamp
670 launcher: launcher
671 panel: panel
672 stage: applicationsDisplayLoader.item
673 }
675 Wizard {
676 id: wizard
677 objectName: "wizard"
678 anchors.fill: parent
679 deferred: shell.mode === "greeter"
681 function unlockWhenDoneWithWizard() {
682 if (!active) {
683 Connectivity.unlockAllModems();
684 }
685 }
687 Component.onCompleted: unlockWhenDoneWithWizard()
688 onActiveChanged: unlockWhenDoneWithWizard()
689 }
691 MouseArea { // modal notifications prevent interacting with other contents
692 anchors.fill: parent
693 visible: notifications.useModal
694 enabled: visible
695 }
697 Notifications {
698 id: notifications
700 model: NotificationBackend.Model
701 margin: units.gu(1)
702 hasMouse: shell.hasMouse
703 background: wallpaperResolver.background
705 y: topmostIsFullscreen ? 0 : panel.panelHeight
706 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
708 states: [
709 State {
710 name: "narrow"
711 when: overlay.width <= units.gu(60)
712 AnchorChanges {
713 target: notifications
714 anchors.left: parent.left
715 anchors.right: parent.right
716 }
717 },
718 State {
719 name: "wide"
720 when: overlay.width > units.gu(60)
721 AnchorChanges {
722 target: notifications
723 anchors.left: undefined
724 anchors.right: parent.right
725 }
726 PropertyChanges { target: notifications; width: units.gu(38) }
727 }
728 ]
729 }
730 }
732 Dialogs {
733 id: dialogs
734 objectName: "dialogs"
735 anchors.fill: parent
736 z: overlay.z + 10
737 usageScenario: shell.usageScenario
738 onPowerOffClicked: {
739 shutdownFadeOutRectangle.enabled = true;
740 shutdownFadeOutRectangle.visible = true;
741 shutdownFadeOut.start();
742 }
743 }
745 Connections {
746 target: SessionBroadcast
747 onShowHome: showHome()
748 }
750 ItemGrabber {
751 id: itemGrabber
752 anchors.fill: parent
753 z: dialogs.z + 10
754 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
755 Connections {
756 target: applicationsDisplayLoader.item
757 ignoreUnknownSignals: true
758 onItemSnapshotRequested: itemGrabber.capture(item)
759 }
760 }
762 Cursor {
763 id: cursor
764 visible: shell.hasMouse
765 z: itemGrabber.z + 1
767 property bool mouseNeverMoved: true
768 Binding {
769 target: cursor; property: "x"; value: shell.width / 2
770 when: cursor.mouseNeverMoved && cursor.visible
771 }
772 Binding {
773 target: cursor; property: "y"; value: shell.height / 2
774 when: cursor.mouseNeverMoved && cursor.visible
775 }
777 height: units.gu(3)
779 onPushedLeftBoundary: {
780 if (buttons === Qt.NoButton) {
781 launcher.pushEdge(amount);
782 }
783 }
785 onPushedRightBoundary: {
786 if (buttons === Qt.NoButton && applicationsDisplayLoader.item
787 && applicationsDisplayLoader.item.pushRightEdge) {
788 applicationsDisplayLoader.item.pushRightEdge(amount);
789 }
790 }
792 onMouseMoved: {
793 mouseNeverMoved = false;
794 cursor.opacity = 1;
795 }
796 }
798 // non-visual object
799 KeymapSwitcher {}
801 Rectangle {
802 id: shutdownFadeOutRectangle
803 z: cursor.z + 1
804 enabled: false
805 visible: false
806 color: "black"
807 anchors.fill: parent
808 opacity: 0.0
809 NumberAnimation on opacity {
810 id: shutdownFadeOut
811 from: 0.0
812 to: 1.0
813 onStopped: {
814 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
815 DBusUnitySessionService.shutdown();
816 }
817 }
818 }
819 }