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