on 660cc95 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 (greeter && greeter.shown) {
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
262 source: "Stages/DesktopStage.qml"
263 readonly property string qmlComponent: {
264 if (shell.mode === "greeter") {
265 return "Stages/AbstractStage.qml"
266 } else if (applicationsDisplayLoader.usageScenario === "phone") {
267 return "Stages/PhoneStage.qml";
268 } else if (applicationsDisplayLoader.usageScenario === "tablet") {
269 return "Stages/TabletStage.qml";
270 } else {
271 return "Stages/DesktopStage.qml";
272 }
273 }
275 property bool interactive: (!greeter || !greeter.shown)
276 && panel.indicators.fullyClosed
277 && launcher.progress == 0
278 && !notifications.useModal
280 onInteractiveChanged: { if (interactive) { focus = true; } }
282 Binding {
283 target: applicationsDisplayLoader.item
284 property: "mode"
285 value: applicationsDisplayLoader.usageScenario == "phone" ? "staged"
286 : applicationsDisplayLoader.usageScenario == "tablet" ? "stagedWithSideStage"
287 : "windowed"
288 }
289 Binding {
290 target: applicationsDisplayLoader.item
291 property: "focus"
292 value: true
293 }
294 Binding {
295 target: applicationsDisplayLoader.item
296 property: "objectName"
297 value: "stage"
298 }
299 Binding {
300 target: applicationsDisplayLoader.item
301 property: "dragAreaWidth"
302 value: shell.edgeSize
303 }
304 Binding {
305 target: applicationsDisplayLoader.item
306 property: "maximizedAppTopMargin"
307 // Not just using panel.panelHeight as that changes depending on the focused app.
308 value: panel.indicators.minimizedPanelHeight
309 }
310 Binding {
311 target: applicationsDisplayLoader.item
312 property: "interactive"
313 value: applicationsDisplayLoader.interactive
314 }
315 Binding {
316 target: applicationsDisplayLoader.item
317 property: "spreadEnabled"
318 value: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
319 }
320 Binding {
321 target: applicationsDisplayLoader.item
322 property: "inverseProgress"
323 value: !greeter || greeter.locked || !tutorial.launcherLongSwipeEnabled ? 0 : launcher.progress
324 }
325 Binding {
326 target: applicationsDisplayLoader.item
327 property: "shellOrientationAngle"
328 value: shell.orientationAngle
329 }
330 Binding {
331 target: applicationsDisplayLoader.item
332 property: "shellOrientation"
333 value: shell.orientation
334 }
335 Binding {
336 target: applicationsDisplayLoader.item
337 property: "orientations"
338 value: shell.orientations
339 }
340 Binding {
341 target: applicationsDisplayLoader.item
342 property: "background"
343 value: wallpaperResolver.background
344 }
345 Binding {
346 target: applicationsDisplayLoader.item
347 property: "nativeWidth"
348 value: shell.nativeWidth
349 }
350 Binding {
351 target: applicationsDisplayLoader.item
352 property: "nativeHeight"
353 value: shell.nativeHeight
354 }
355 Binding {
356 target: applicationsDisplayLoader.item
357 property: "beingResized"
358 value: shell.beingResized
359 }
360 Binding {
361 target: applicationsDisplayLoader.item
362 property: "keepDashRunning"
363 value: launcher.shown || launcher.dashSwipe
364 }
365 Binding {
366 target: applicationsDisplayLoader.item
367 property: "suspended"
368 value: greeter.shown
369 }
370 Binding {
371 target: applicationsDisplayLoader.item
372 property: "altTabPressed"
373 value: physicalKeysMapper.altTabPressed
374 }
375 Binding {
376 target: applicationsDisplayLoader.item
377 property: "leftMargin"
378 value: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
379 }
380 Binding {
381 target: applicationsDisplayLoader.item
382 property: "applicationManager"
383 value: ApplicationManager
384 }
385 Binding {
386 target: applicationsDisplayLoader.item
387 property: "topLevelSurfaceList"
388 value: topLevelSurfaceList
389 }
390 Binding {
391 target: applicationsDisplayLoader.item
392 property: "oskEnabled"
393 value: shell.oskEnabled
394 }
395 }
396 }
398 InputMethod {
399 id: inputMethod
400 objectName: "inputMethod"
401 anchors {
402 fill: parent
403 topMargin: panel.panelHeight
404 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
405 }
406 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
407 }
409 Loader {
410 id: greeterLoader
411 anchors.fill: parent
412 anchors.topMargin: panel.panelHeight
413 sourceComponent: shell.mode != "shell" ? integratedGreeter :
414 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
415 onLoaded: {
416 item.objectName = "greeter"
417 }
418 }
420 Component {
421 id: integratedGreeter
422 Greeter {
424 hides: [launcher, panel.indicators]
425 tabletMode: shell.usageScenario != "phone"
426 launcherOffset: launcher.progress
427 forcedUnlock: wizard.active || shell.mode === "full-shell"
428 background: wallpaperResolver.background
429 allowFingerprint: !dialogs.hasActiveDialog &&
430 !notifications.topmostIsFullscreen &&
431 !panel.indicators.shown
433 // avoid overlapping with Launcher's edge drag area
434 // FIXME: Fix TouchRegistry & friends and remove this workaround
435 // Issue involves launcher's DDA getting disabled on a long
436 // left-edge drag
437 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
439 onSessionStarted: {
440 launcher.hide();
441 }
443 onTease: {
444 if (!tutorial.running) {
445 launcher.tease();
446 }
447 }
449 onEmergencyCall: startLockedApp("dialer-app")
450 }
451 }
453 Timer {
454 // See powerConnection for why this is useful
455 id: showGreeterDelayed
456 interval: 1
457 onTriggered: {
458 greeter.forceShow();
459 }
460 }
462 Connections {
463 id: callConnection
464 target: callManager
466 onHasCallsChanged: {
467 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
468 // We just received an incoming call while locked. The
469 // indicator will have already launched dialer-app for us, but
470 // there is a race between "hasCalls" changing and the dialer
471 // starting up. So in case we lose that race, we'll start/
472 // focus the dialer ourselves here too. Even if the indicator
473 // didn't launch the dialer for some reason (or maybe a call
474 // started via some other means), if an active call is
475 // happening, we want to be in the dialer.
476 startLockedApp("dialer-app")
477 }
478 }
479 }
481 Connections {
482 id: powerConnection
483 target: Powerd
485 onStatusChanged: {
486 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
487 !callManager.hasCalls && !wizard.active) {
488 // We don't want to simply call greeter.showNow() here, because
489 // that will take too long. Qt will delay button event
490 // handling until the greeter is done loading and may think the
491 // user held down the power button the whole time, leading to a
492 // power dialog being shown. Instead, delay showing the
493 // greeter until we've finished handling the event. We could
494 // make the greeter load asynchronously instead, but that
495 // introduces a whole host of timing issues, especially with
496 // its animations. So this is simpler.
497 showGreeterDelayed.start();
498 }
499 }
500 }
502 function showHome() {
503 greeter.notifyUserRequestedApp("unity8-dash");
505 var animate = !LightDMService.greeter.active && !stages.shown
506 dash.setCurrentScope(0, animate, false)
507 ApplicationManager.requestFocusApplication("unity8-dash")
508 }
510 function showDash() {
511 if (greeter.notifyShowingDashFromDrag()) {
512 launcher.fadeOut();
513 }
515 if (!greeter.locked && tutorial.launcherLongSwipeEnabled
516 && ApplicationManager.focusedApplicationId != "unity8-dash") {
517 ApplicationManager.requestFocusApplication("unity8-dash")
518 launcher.fadeOut();
519 }
520 }
522 Item {
523 id: overlay
524 z: 10
526 anchors.fill: parent
528 Panel {
529 id: panel
530 objectName: "panel"
531 anchors.fill: parent //because this draws indicator menus
532 indicators {
533 hides: [launcher]
534 available: tutorial.panelEnabled
535 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
536 && (!greeter || !greeter.hasLockedApp)
537 && !shell.waitingOnGreeter
538 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
540 minimizedPanelHeight: units.gu(3)
541 expandedPanelHeight: units.gu(7)
543 indicatorsModel: Indicators.IndicatorsModel {
544 // tablet and phone both use the same profile
545 // FIXME: use just "phone" for greeter too, but first fix
546 // greeter app launching to either load the app inside the
547 // greeter or tell the session to load the app. This will
548 // involve taking the url-dispatcher dbus name and using
549 // SessionBroadcast to tell the session.
550 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
551 Component.onCompleted: load();
552 }
553 }
555 callHint {
556 greeterShown: greeter.shown
557 }
559 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
560 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
561 : false
562 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
563 || greeter.hasLockedApp
564 locked: greeter && greeter.active
565 }
567 Launcher {
568 id: launcher
569 objectName: "launcher"
571 /*
572 * Since the Dash doesn't have the same controll over surfaces that the
573 * Shell does, it can't slowly move the scope out of the way, as the shell
574 * does with apps, and the dash is show instantly. This allows for some
575 * leeway and prevents accidental home swipes.
576 */
577 readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
578 readonly property bool dashSwipe: progress > offset
580 anchors.top: parent.top
581 anchors.topMargin: inverted ? 0 : panel.panelHeight
582 anchors.bottom: parent.bottom
583 width: parent.width
584 dragAreaWidth: shell.edgeSize
585 available: tutorial.launcherEnabled
586 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
587 && !greeter.hasLockedApp
588 && !shell.waitingOnGreeter
589 inverted: shell.usageScenario !== "desktop"
590 superPressed: physicalKeysMapper.superPressed
591 superTabPressed: physicalKeysMapper.superTabPressed
592 panelWidth: units.gu(settings.launcherWidth)
593 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
595 onShowDashHome: showHome()
596 onDash: showDash()
597 onDashSwipeChanged: {
598 if (dashSwipe) {
599 dash.setCurrentScope(0, false, true)
600 }
601 }
602 onLauncherApplicationSelected: {
603 greeter.notifyUserRequestedApp(appId);
604 shell.activateApplication(appId);
605 }
606 onShownChanged: {
607 if (shown) {
608 panel.indicators.hide()
609 }
610 }
611 onFocusChanged: {
612 if (!focus) {
613 applicationsDisplayLoader.focus = true;
614 }
615 }
617 GlobalShortcut {
618 shortcut: Qt.AltModifier | Qt.Key_F1
619 onTriggered: {
620 launcher.openForKeyboardNavigation();
621 }
622 }
623 GlobalShortcut {
624 shortcut: Qt.MetaModifier | Qt.Key_0
625 onTriggered: {
626 if (LauncherModel.get(9)) {
627 activateApplication(LauncherModel.get(9).appId);
628 }
629 }
630 }
631 Repeater {
632 model: 9
633 GlobalShortcut {
634 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
635 onTriggered: {
636 if (LauncherModel.get(index)) {
637 activateApplication(LauncherModel.get(index).appId);
638 }
639 }
640 }
641 }
642 }
644 KeyboardShortcutsOverlay {
645 objectName: "shortcutsOverlay"
646 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
647 && height < parent.height - padding - panel.panelHeight
648 anchors.centerIn: parent
649 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
650 anchors.verticalCenterOffset: panel.panelHeight/2
651 visible: opacity > 0
652 opacity: enabled ? 0.95 : 0
654 Behavior on opacity {
655 UbuntuNumberAnimation {}
656 }
657 }
659 Tutorial {
660 id: tutorial
661 objectName: "tutorial"
662 anchors.fill: parent
664 paused: callManager.hasCalls || !greeter || greeter.shown ||
665 wizard.active
666 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
667 inputMethod.visible ||
668 (launcher.shown && !launcher.lockedVisible) ||
669 panel.indicators.shown || stage.dragProgress > 0
670 usageScenario: shell.usageScenario
671 lastInputTimestamp: inputFilter.lastInputTimestamp
672 launcher: launcher
673 panel: panel
674 stage: applicationsDisplayLoader.item
675 }
677 Wizard {
678 id: wizard
679 objectName: "wizard"
680 anchors.fill: parent
681 deferred: shell.mode === "greeter"
683 function unlockWhenDoneWithWizard() {
684 if (!active) {
685 Connectivity.unlockAllModems();
686 }
687 }
689 Component.onCompleted: unlockWhenDoneWithWizard()
690 onActiveChanged: unlockWhenDoneWithWizard()
691 }
693 MouseArea { // modal notifications prevent interacting with other contents
694 anchors.fill: parent
695 visible: notifications.useModal
696 enabled: visible
697 }
699 Notifications {
700 id: notifications
702 model: NotificationBackend.Model
703 margin: units.gu(1)
704 hasMouse: shell.hasMouse
705 background: wallpaperResolver.background
707 y: topmostIsFullscreen ? 0 : panel.panelHeight
708 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
710 states: [
711 State {
712 name: "narrow"
713 when: overlay.width <= units.gu(60)
714 AnchorChanges {
715 target: notifications
716 anchors.left: parent.left
717 anchors.right: parent.right
718 }
719 },
720 State {
721 name: "wide"
722 when: overlay.width > units.gu(60)
723 AnchorChanges {
724 target: notifications
725 anchors.left: undefined
726 anchors.right: parent.right
727 }
728 PropertyChanges { target: notifications; width: units.gu(38) }
729 }
730 ]
731 }
732 }
734 Dialogs {
735 id: dialogs
736 objectName: "dialogs"
737 anchors.fill: parent
738 z: overlay.z + 10
739 usageScenario: shell.usageScenario
740 onPowerOffClicked: {
741 shutdownFadeOutRectangle.enabled = true;
742 shutdownFadeOutRectangle.visible = true;
743 shutdownFadeOut.start();
744 }
745 }
747 Connections {
748 target: SessionBroadcast
749 onShowHome: showHome()
750 }
752 ItemGrabber {
753 id: itemGrabber
754 anchors.fill: parent
755 z: dialogs.z + 10
756 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
757 Connections {
758 target: applicationsDisplayLoader.item
759 ignoreUnknownSignals: true
760 onItemSnapshotRequested: itemGrabber.capture(item)
761 }
762 }
764 Cursor {
765 id: cursor
766 visible: shell.hasMouse
767 z: itemGrabber.z + 1
769 property bool mouseNeverMoved: true
770 Binding {
771 target: cursor; property: "x"; value: shell.width / 2
772 when: cursor.mouseNeverMoved && cursor.visible
773 }
774 Binding {
775 target: cursor; property: "y"; value: shell.height / 2
776 when: cursor.mouseNeverMoved && cursor.visible
777 }
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 }