on 7002f3c help sign in
unity8/qml/Shell.qml
812 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 // Disable everything while greeter is waiting, so that the user can't swipe
141 // the greeter or launcher until we know whether the session is locked.
142 enabled: 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 }
390 }
392 InputMethod {
393 id: inputMethod
394 objectName: "inputMethod"
395 anchors {
396 fill: parent
397 topMargin: panel.panelHeight
398 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
399 }
400 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
401 }
403 Loader {
404 id: greeterLoader
405 anchors.fill: parent
406 anchors.topMargin: panel.panelHeight
407 sourceComponent: shell.mode != "shell" ? integratedGreeter :
408 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
409 onLoaded: {
410 item.objectName = "greeter"
411 }
412 }
414 Component {
415 id: integratedGreeter
416 Greeter {
418 hides: [launcher, panel.indicators]
419 tabletMode: shell.usageScenario != "phone"
420 launcherOffset: launcher.progress
421 forcedUnlock: wizard.active || shell.mode === "full-shell"
422 background: wallpaperResolver.background
423 allowFingerprint: !dialogs.hasActiveDialog &&
424 !notifications.topmostIsFullscreen &&
425 !panel.indicators.shown
427 // avoid overlapping with Launcher's edge drag area
428 // FIXME: Fix TouchRegistry & friends and remove this workaround
429 // Issue involves launcher's DDA getting disabled on a long
430 // left-edge drag
431 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
433 onSessionStarted: {
434 launcher.hide();
435 }
437 onTease: {
438 if (!tutorial.running) {
439 launcher.tease();
440 }
441 }
443 onEmergencyCall: startLockedApp("dialer-app")
444 }
445 }
447 Timer {
448 // See powerConnection for why this is useful
449 id: showGreeterDelayed
450 interval: 1
451 onTriggered: {
452 greeter.forceShow();
453 }
454 }
456 Connections {
457 id: callConnection
458 target: callManager
460 onHasCallsChanged: {
461 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
462 // We just received an incoming call while locked. The
463 // indicator will have already launched dialer-app for us, but
464 // there is a race between "hasCalls" changing and the dialer
465 // starting up. So in case we lose that race, we'll start/
466 // focus the dialer ourselves here too. Even if the indicator
467 // didn't launch the dialer for some reason (or maybe a call
468 // started via some other means), if an active call is
469 // happening, we want to be in the dialer.
470 startLockedApp("dialer-app")
471 }
472 }
473 }
475 Connections {
476 id: powerConnection
477 target: Powerd
479 onStatusChanged: {
480 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
481 !callManager.hasCalls && !wizard.active) {
482 // We don't want to simply call greeter.showNow() here, because
483 // that will take too long. Qt will delay button event
484 // handling until the greeter is done loading and may think the
485 // user held down the power button the whole time, leading to a
486 // power dialog being shown. Instead, delay showing the
487 // greeter until we've finished handling the event. We could
488 // make the greeter load asynchronously instead, but that
489 // introduces a whole host of timing issues, especially with
490 // its animations. So this is simpler.
491 showGreeterDelayed.start();
492 }
493 }
494 }
496 function showHome() {
497 greeter.notifyUserRequestedApp("unity8-dash");
499 var animate = !LightDMService.greeter.active && !stages.shown
500 dash.setCurrentScope(0, animate, false)
501 ApplicationManager.requestFocusApplication("unity8-dash")
502 }
504 function showDash() {
505 if (greeter.notifyShowingDashFromDrag()) {
506 launcher.fadeOut();
507 }
509 if (!greeter.locked && tutorial.launcherLongSwipeEnabled
510 && ApplicationManager.focusedApplicationId != "unity8-dash") {
511 ApplicationManager.requestFocusApplication("unity8-dash")
512 launcher.fadeOut();
513 }
514 }
516 Item {
517 id: overlay
518 z: 10
520 anchors.fill: parent
522 Panel {
523 id: panel
524 objectName: "panel"
525 anchors.fill: parent //because this draws indicator menus
526 indicators {
527 hides: [launcher]
528 available: tutorial.panelEnabled
529 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
530 && (!greeter || !greeter.hasLockedApp)
531 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
533 minimizedPanelHeight: units.gu(3)
534 expandedPanelHeight: units.gu(7)
536 indicatorsModel: Indicators.IndicatorsModel {
537 // tablet and phone both use the same profile
538 // FIXME: use just "phone" for greeter too, but first fix
539 // greeter app launching to either load the app inside the
540 // greeter or tell the session to load the app. This will
541 // involve taking the url-dispatcher dbus name and using
542 // SessionBroadcast to tell the session.
543 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
544 Component.onCompleted: load();
545 }
546 }
548 callHint {
549 greeterShown: greeter.shown
550 }
552 readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
553 ? MirFocusController.focusedSurface.state === Mir.FullscreenState
554 : false
555 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
556 || greeter.hasLockedApp
557 locked: greeter && greeter.active
558 }
560 Launcher {
561 id: launcher
562 objectName: "launcher"
564 /*
565 * Since the Dash doesn't have the same controll over surfaces that the
566 * Shell does, it can't slowly move the scope out of the way, as the shell
567 * does with apps, and the dash is show instantly. This allows for some
568 * leeway and prevents accidental home swipes.
569 */
570 readonly property real offset: shell.focusedApplicationId == "unity8-dash" ? units.gu(12) : 0
571 readonly property bool dashSwipe: progress > offset
573 anchors.top: parent.top
574 anchors.topMargin: inverted ? 0 : panel.panelHeight
575 anchors.bottom: parent.bottom
576 width: parent.width
577 dragAreaWidth: shell.edgeSize
578 available: tutorial.launcherEnabled
579 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
580 && !greeter.hasLockedApp
581 inverted: shell.usageScenario !== "desktop"
582 superPressed: physicalKeysMapper.superPressed
583 superTabPressed: physicalKeysMapper.superTabPressed
584 panelWidth: units.gu(settings.launcherWidth)
585 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
587 onShowDashHome: showHome()
588 onDash: showDash()
589 onDashSwipeChanged: {
590 if (dashSwipe) {
591 dash.setCurrentScope(0, false, true)
592 }
593 }
594 onLauncherApplicationSelected: {
595 greeter.notifyUserRequestedApp(appId);
596 shell.activateApplication(appId);
597 }
598 onShownChanged: {
599 if (shown) {
600 panel.indicators.hide()
601 }
602 }
603 onFocusChanged: {
604 if (!focus) {
605 applicationsDisplayLoader.focus = true;
606 }
607 }
609 GlobalShortcut {
610 shortcut: Qt.AltModifier | Qt.Key_F1
611 onTriggered: {
612 launcher.openForKeyboardNavigation();
613 }
614 }
615 GlobalShortcut {
616 shortcut: Qt.MetaModifier | Qt.Key_0
617 onTriggered: {
618 if (LauncherModel.get(9)) {
619 activateApplication(LauncherModel.get(9).appId);
620 }
621 }
622 }
623 Repeater {
624 model: 9
625 GlobalShortcut {
626 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
627 onTriggered: {
628 if (LauncherModel.get(index)) {
629 activateApplication(LauncherModel.get(index).appId);
630 }
631 }
632 }
633 }
634 }
636 KeyboardShortcutsOverlay {
637 objectName: "shortcutsOverlay"
638 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
639 && height < parent.height - padding - panel.panelHeight
640 anchors.centerIn: parent
641 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
642 anchors.verticalCenterOffset: panel.panelHeight/2
643 visible: opacity > 0
644 opacity: enabled ? 0.95 : 0
646 Behavior on opacity {
647 UbuntuNumberAnimation {}
648 }
649 }
651 Tutorial {
652 id: tutorial
653 objectName: "tutorial"
654 anchors.fill: parent
656 paused: callManager.hasCalls || !greeter || greeter.shown ||
657 wizard.active
658 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
659 inputMethod.state === "shown" ||
660 (launcher.shown && !launcher.lockedVisible) ||
661 panel.indicators.shown || stage.dragProgress > 0
662 usageScenario: shell.usageScenario
663 lastInputTimestamp: inputFilter.lastInputTimestamp
664 launcher: launcher
665 panel: panel
666 stage: applicationsDisplayLoader.item
667 }
669 Wizard {
670 id: wizard
671 objectName: "wizard"
672 anchors.fill: parent
673 deferred: shell.mode === "greeter"
675 function unlockWhenDoneWithWizard() {
676 if (!active) {
677 Connectivity.unlockAllModems();
678 }
679 }
681 Component.onCompleted: unlockWhenDoneWithWizard()
682 onActiveChanged: unlockWhenDoneWithWizard()
683 }
685 MouseArea { // modal notifications prevent interacting with other contents
686 anchors.fill: parent
687 visible: notifications.useModal
688 enabled: visible
689 }
691 Notifications {
692 id: notifications
694 model: NotificationBackend.Model
695 margin: units.gu(1)
696 hasMouse: shell.hasMouse
697 background: wallpaperResolver.background
699 y: topmostIsFullscreen ? 0 : panel.panelHeight
700 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
702 states: [
703 State {
704 name: "narrow"
705 when: overlay.width <= units.gu(60)
706 AnchorChanges {
707 target: notifications
708 anchors.left: parent.left
709 anchors.right: parent.right
710 }
711 },
712 State {
713 name: "wide"
714 when: overlay.width > units.gu(60)
715 AnchorChanges {
716 target: notifications
717 anchors.left: undefined
718 anchors.right: parent.right
719 }
720 PropertyChanges { target: notifications; width: units.gu(38) }
721 }
722 ]
723 }
724 }
726 Dialogs {
727 id: dialogs
728 objectName: "dialogs"
729 anchors.fill: parent
730 z: overlay.z + 10
731 usageScenario: shell.usageScenario
732 onPowerOffClicked: {
733 shutdownFadeOutRectangle.enabled = true;
734 shutdownFadeOutRectangle.visible = true;
735 shutdownFadeOut.start();
736 }
737 }
739 Connections {
740 target: SessionBroadcast
741 onShowHome: showHome()
742 }
744 ItemGrabber {
745 id: itemGrabber
746 anchors.fill: parent
747 z: dialogs.z + 10
748 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
749 Connections {
750 target: applicationsDisplayLoader.item
751 ignoreUnknownSignals: true
752 onItemSnapshotRequested: itemGrabber.capture(item)
753 }
754 }
756 Cursor {
757 id: cursor
758 visible: shell.hasMouse
759 z: itemGrabber.z + 1
761 property bool mouseNeverMoved: true
762 Binding {
763 target: cursor; property: "x"; value: shell.width / 2
764 when: cursor.mouseNeverMoved && cursor.visible
765 }
766 Binding {
767 target: cursor; property: "y"; value: shell.height / 2
768 when: cursor.mouseNeverMoved && cursor.visible
769 }
771 onPushedLeftBoundary: {
772 if (buttons === Qt.NoButton) {
773 launcher.pushEdge(amount);
774 }
775 }
777 onPushedRightBoundary: {
778 if (buttons === Qt.NoButton && applicationsDisplayLoader.item
779 && applicationsDisplayLoader.item.pushRightEdge) {
780 applicationsDisplayLoader.item.pushRightEdge(amount);
781 }
782 }
784 onMouseMoved: {
785 mouseNeverMoved = false;
786 cursor.opacity = 1;
787 }
788 }
790 // non-visual object
791 KeymapSwitcher {}
793 Rectangle {
794 id: shutdownFadeOutRectangle
795 z: cursor.z + 1
796 enabled: false
797 visible: false
798 color: "black"
799 anchors.fill: parent
800 opacity: 0.0
801 NumberAnimation on opacity {
802 id: shutdownFadeOut
803 from: 0.0
804 to: 1.0
805 onStopped: {
806 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
807 DBusUnitySessionService.shutdown();
808 }
809 }
810 }
811 }