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