on f661fa4 help sign in
unity8/qml/Shell.qml
908 lines 32 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 ImageCache 0.1
30import Utils 0.1
31import Powerd 0.1
32import SessionBroadcast 0.1
33import "Greeter"
34import "Launcher"
35import "Panel"
36import "Components"
37import "Notifications"
38import "Stage"
39import "Tutorial"
40import "Wizard"
41import "Components/PanelState"
42import Unity.Notifications 1.0 as NotificationBackend
43import Unity.Session 0.1
44import Unity.DashCommunicator 0.1
45import Unity.Indicators 0.1 as Indicators
46import Cursor 1.1
47import WindowManager 1.0
50StyledItem {
51 id: shell
53 theme.name: "Ubuntu.Components.Themes.SuruDark"
55 // to be set from outside
56 property int orientationAngle: 0
57 property int orientation
58 property Orientations orientations
59 property real nativeWidth
60 property real nativeHeight
61 property alias panelAreaShowProgress: panel.panelAreaShowProgress
62 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
63 property string mode: "full-greeter"
64 property alias oskEnabled: inputMethod.enabled
65 function updateFocusedAppOrientation() {
66 stage.updateFocusedAppOrientation();
67 }
68 function updateFocusedAppOrientationAnimated() {
69 stage.updateFocusedAppOrientationAnimated();
70 }
71 property bool hasMouse: false
72 property bool hasKeyboard: false
73 property bool hasTouchscreen: false
75 // to be read from outside
76 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
78 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
79 && stage.orientationChangesEnabled
80 && (!greeter || !greeter.animating)
82 readonly property bool showingGreeter: greeter && greeter.shown
84 property bool startingUp: true
85 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
87 property int supportedOrientations: {
88 if (startingUp) {
89 // Ensure we don't rotate during start up
90 return Qt.PrimaryOrientation;
91 } else if (showingGreeter || notifications.topmostIsFullscreen) {
92 return Qt.PrimaryOrientation;
93 } else {
94 return shell.orientations.map(stage.supportedOrientations);
95 }
96 }
98 readonly property var mainApp: stage.mainApp
100 readonly property var topLevelSurfaceList: {
101 if (!WMScreen.currentWorkspace) return null;
102 return WMScreen.currentWorkspace.windowModel
103 }
105 onMainAppChanged: {
106 if (mainApp) {
107 _onMainAppChanged(mainApp.appId);
108 }
109 }
110 Connections {
111 target: ApplicationManager
112 onFocusRequested: {
113 if (shell.mainApp && shell.mainApp.appId === appId) {
114 _onMainAppChanged(appId);
115 }
116 }
117 }
118 function _onMainAppChanged(appId) {
119 if (wizard.active && appId != "" && appId != "unity8-dash") {
120 // If this happens on first boot, we may be in the
121 // wizard while receiving a call. But a call is more
122 // important than the wizard so just bail out of it.
123 wizard.hide();
124 }
126 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
127 // If we are in the middle of a call, make dialer lockedApp and show it.
128 // This can happen if user backs out of dialer back to greeter, then
129 // launches dialer again.
130 greeter.lockedApp = appId;
131 }
132 greeter.notifyAppFocusRequested(appId);
134 panel.indicators.hide();
135 launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
136 }
138 // For autopilot consumption
139 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
141 // Note when greeter is waiting on PAM, so that we can disable edges until
142 // we know which user data to show and whether the session is locked.
143 readonly property bool waitingOnGreeter: greeter && greeter.waiting
145 property real edgeSize: units.gu(settings.edgeDragWidth)
147 WallpaperResolver {
148 id: wallpaperResolver
149 objectName: "wallpaperResolver"
151 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
152 readonly property bool hasCustomBackground: background != defaultBackground
154 // Use a cached version of the scaled-down wallpaper (as sometimes the
155 // image can be quite big compared to the device size, including for
156 // our default wallpaper). We use a name=wallpaper argument here to
157 // make sure we don't litter our cache with lots of scaled images. We
158 // only need to bother caching one at a time.
159 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
161 GSettings {
162 id: backgroundSettings
163 schema.id: "org.gnome.desktop.background"
164 }
166 candidates: [
167 AccountsService.backgroundFile,
168 backgroundSettings.pictureUri,
169 defaultBackground
170 ]
171 }
173 readonly property alias greeter: greeterLoader.item
175 function activateApplication(appId) {
176 // Either open the app in our own session, or -- if we're acting as a
177 // greeter -- ask the user's session to open it for us.
178 if (shell.mode === "greeter") {
179 activateURL("application:///" + appId + ".desktop");
180 } else {
181 startApp(appId);
182 }
183 }
185 function activateURL(url) {
186 SessionBroadcast.requestUrlStart(AccountsService.user, url);
187 greeter.notifyUserRequestedApp();
188 panel.indicators.hide();
189 }
191 function startApp(appId) {
192 if (ApplicationManager.findApplication(appId)) {
193 ApplicationManager.requestFocusApplication(appId);
194 } else {
195 print("calling startApplication. active workspace is", WorkspaceManager.activeWorkspace)
196 ApplicationManager.startApplication(appId);
197 }
198 }
200 function startLockedApp(app) {
201 if (greeter.locked) {
202 greeter.lockedApp = app;
203 }
204 startApp(app); // locked apps are always in our same session
205 }
207 Binding {
208 target: LauncherModel
209 property: "applicationManager"
210 value: ApplicationManager
211 }
213 Component.onCompleted: {
214 finishStartUpTimer.start();
215 }
217 VolumeControl {
218 id: volumeControl
219 }
221 DashCommunicator {
222 id: dash
223 objectName: "dashCommunicator"
224 }
226 PhysicalKeysMapper {
227 id: physicalKeysMapper
228 objectName: "physicalKeysMapper"
230 onPowerKeyLongPressed: dialogs.showPowerDialog();
231 onVolumeDownTriggered: volumeControl.volumeDown();
232 onVolumeUpTriggered: volumeControl.volumeUp();
233 onScreenshotTriggered: itemGrabber.capture(shell);
234 }
236 GlobalShortcut {
237 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
238 }
240 WindowInputFilter {
241 id: inputFilter
242 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
243 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
244 }
246 WindowInputMonitor {
247 objectName: "windowInputMonitor"
248 onHomeKeyActivated: {
249 // Ignore when greeter is active, to avoid pocket presses
250 if (!greeter.active) {
251 launcher.fadeOut();
252 ApplicationManager.requestFocusApplication("unity8-dash");
253 }
254 }
255 onTouchBegun: { cursor.opacity = 0; }
256 onTouchEnded: {
257 // move the (hidden) cursor to the last known touch position
258 var mappedCoords = mapFromItem(null, pos.x, pos.y);
259 cursor.x = mappedCoords.x;
260 cursor.y = mappedCoords.y;
261 cursor.mouseNeverMoved = false;
262 }
263 }
265 GSettings {
266 id: settings
267 schema.id: "com.canonical.Unity8"
268 }
270 PanelState {
271 id: panelState
272 objectName: "panelState"
273 }
275 Item {
276 id: stages
277 objectName: "stages"
278 width: parent.width
279 height: parent.height
281 Stage {
282 id: stage
283 objectName: "stage"
284 anchors.fill: parent
285 focus: true
287 dragAreaWidth: shell.edgeSize
288 background: wallpaperResolver.background
290 applicationManager: ApplicationManager
291 topLevelSurfaceList: shell.topLevelSurfaceList
292 inputMethodRect: inputMethod.visibleRect
293 rightEdgePushProgress: rightEdgeBarrier.progress
295 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
296 ? "phone"
297 : shell.usageScenario
299 mode: usageScenario == "phone" ? "staged"
300 : usageScenario == "tablet" ? "stagedWithSideStage"
301 : "windowed"
303 shellOrientation: shell.orientation
304 shellOrientationAngle: shell.orientationAngle
305 orientations: shell.orientations
306 nativeWidth: shell.nativeWidth
307 nativeHeight: shell.nativeHeight
309 interactive: (!greeter || !greeter.shown)
310 && panel.indicators.fullyClosed
311 && !notifications.useModal
313 onInteractiveChanged: { if (interactive) { focus = true; } }
315 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
316 suspended: greeter.shown
317 altTabPressed: physicalKeysMapper.altTabPressed
318 oskEnabled: shell.oskEnabled
319 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
320 panelState: panelState
322 onSpreadShownChanged: {
323 panel.indicators.hide();
324 panel.applicationMenus.hide();
325 }
326 }
328 TouchGestureArea {
329 anchors.fill: stage
331 minimumTouchPoints: 4
332 maximumTouchPoints: minimumTouchPoints
334 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
335 touchPoints.length >= minimumTouchPoints &&
336 touchPoints.length <= maximumTouchPoints
337 property bool wasPressed: false
339 onRecognisedPressChanged: {
340 if (recognisedPress) {
341 wasPressed = true;
342 }
343 }
345 onStatusChanged: {
346 if (status !== TouchGestureArea.Recognized) {
347 if (status === TouchGestureArea.WaitingForTouch) {
348 if (wasPressed && !dragging) {
349 launcher.openDrawer(true);
350 }
351 }
352 wasPressed = false;
353 }
354 }
355 }
356 }
358 InputMethod {
359 id: inputMethod
360 objectName: "inputMethod"
361 surface: shell.topLevelSurfaceList.inputMethodSurface
362 anchors {
363 fill: parent
364 topMargin: panel.panelHeight
365 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
366 }
367 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
368 }
370 Loader {
371 id: greeterLoader
372 objectName: "greeterLoader"
373 anchors.fill: parent
374 anchors.topMargin: panel.panelHeight
375 sourceComponent: {
376 if (shell.mode != "shell") {
377 if (screenWindow.primary) return integratedGreeter;
378 return secondaryGreeter;
379 }
380 return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
381 }
382 onLoaded: {
383 item.objectName = "greeter"
384 }
385 property bool openDrawerAfterUnlock: false
386 Connections {
387 target: greeter
388 onActiveChanged: {
389 if (!greeter.active && greeterLoader.openDrawerAfterUnlock) {
390 launcher.openDrawer(false);
391 greeterLoader.openDrawerAfterUnlock = false;
392 }
393 }
394 }
395 }
397 Component {
398 id: integratedGreeter
399 Greeter {
401 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
402 hides: [launcher, panel.indicators, panel.applicationMenus]
403 tabletMode: shell.usageScenario != "phone"
404 forcedUnlock: wizard.active || shell.mode === "full-shell"
405 background: wallpaperResolver.cachedBackground
406 hasCustomBackground: wallpaperResolver.hasCustomBackground
407 allowFingerprint: !dialogs.hasActiveDialog &&
408 !notifications.topmostIsFullscreen &&
409 !panel.indicators.shown
411 // avoid overlapping with Launcher's edge drag area
412 // FIXME: Fix TouchRegistry & friends and remove this workaround
413 // Issue involves launcher's DDA getting disabled on a long
414 // left-edge drag
415 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
417 onSessionStarted: {
418 launcher.hide();
419 }
421 onTease: {
422 if (!tutorial.running) {
423 launcher.tease();
424 }
425 }
427 onEmergencyCall: startLockedApp("dialer-app")
428 }
429 }
431 Component {
432 id: secondaryGreeter
433 SecondaryGreeter {
434 hides: [launcher, panel.indicators]
435 }
436 }
438 Timer {
439 // See powerConnection for why this is useful
440 id: showGreeterDelayed
441 interval: 1
442 onTriggered: {
443 // Go through the dbus service, because it has checks for whether
444 // we are even allowed to lock or not.
445 DBusUnitySessionService.PromptLock();
446 }
447 }
449 Connections {
450 id: callConnection
451 target: callManager
453 onHasCallsChanged: {
454 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
455 // We just received an incoming call while locked. The
456 // indicator will have already launched dialer-app for us, but
457 // there is a race between "hasCalls" changing and the dialer
458 // starting up. So in case we lose that race, we'll start/
459 // focus the dialer ourselves here too. Even if the indicator
460 // didn't launch the dialer for some reason (or maybe a call
461 // started via some other means), if an active call is
462 // happening, we want to be in the dialer.
463 startLockedApp("dialer-app")
464 }
465 }
466 }
468 Connections {
469 id: powerConnection
470 target: Powerd
472 onStatusChanged: {
473 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
474 !callManager.hasCalls && !wizard.active) {
475 // We don't want to simply call greeter.showNow() here, because
476 // that will take too long. Qt will delay button event
477 // handling until the greeter is done loading and may think the
478 // user held down the power button the whole time, leading to a
479 // power dialog being shown. Instead, delay showing the
480 // greeter until we've finished handling the event. We could
481 // make the greeter load asynchronously instead, but that
482 // introduces a whole host of timing issues, especially with
483 // its animations. So this is simpler.
484 showGreeterDelayed.start();
485 }
486 }
487 }
489 function showHome() {
490 greeter.notifyUserRequestedApp();
492 if (shell.mode === "greeter") {
493 SessionBroadcast.requestHomeShown(AccountsService.user);
494 } else {
495 if (!greeter.active) {
496 launcher.openDrawer(false);
497 } else {
498 greeterLoader.openDrawerAfterUnlock = true;
499 }
500 }
501 }
503 Item {
504 id: overlay
505 z: 10
507 anchors.fill: parent
509 Panel {
510 id: panel
511 objectName: "panel"
512 anchors.fill: parent //because this draws indicator menus
514 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
515 minimizedPanelHeight: units.gu(3)
516 expandedPanelHeight: units.gu(7)
517 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
518 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
519 applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
521 indicators {
522 hides: [launcher]
523 available: tutorial.panelEnabled
524 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
525 && (!greeter || !greeter.hasLockedApp)
526 && !shell.waitingOnGreeter
527 && settings.enableIndicatorMenu
529 model: Indicators.IndicatorsModel {
530 // tablet and phone both use the same profile
531 // FIXME: use just "phone" for greeter too, but first fix
532 // greeter app launching to either load the app inside the
533 // greeter or tell the session to load the app. This will
534 // involve taking the url-dispatcher dbus name and using
535 // SessionBroadcast to tell the session.
536 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
537 Component.onCompleted: load();
538 }
539 }
541 applicationMenus {
542 hides: [launcher]
543 available: (!greeter || !greeter.shown)
544 && !shell.waitingOnGreeter
545 && !stage.spreadShown
546 }
548 readonly property bool focusedSurfaceIsFullscreen: shell.topLevelSurfaceList.focusedWindow
549 ? shell.topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
550 : false
551 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
552 || greeter.hasLockedApp
553 greeterShown: greeter && greeter.shown
554 hasKeyboard: shell.hasKeyboard
555 panelState: panelState
556 }
558 Launcher {
559 id: launcher
560 objectName: "launcher"
562 anchors.top: parent.top
563 anchors.topMargin: inverted ? 0 : panel.panelHeight
564 anchors.bottom: parent.bottom
565 width: parent.width
566 dragAreaWidth: shell.edgeSize
567 available: tutorial.launcherEnabled
568 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
569 && !greeter.hasLockedApp
570 && !shell.waitingOnGreeter
571 && settings.enableLauncher
572 inverted: shell.usageScenario !== "desktop"
573 superPressed: physicalKeysMapper.superPressed
574 superTabPressed: physicalKeysMapper.superTabPressed
575 panelWidth: units.gu(settings.launcherWidth)
576 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
577 blurSource: greeter.shown ? greeter : stages
578 topPanelHeight: panel.panelHeight
579 drawerEnabled: !greeter.active
580 privateMode: greeter.active
582 onShowDashHome: showHome()
583 onLauncherApplicationSelected: {
584 greeter.notifyUserRequestedApp();
585 shell.activateApplication(appId);
586 }
587 onShownChanged: {
588 if (shown) {
589 panel.indicators.hide();
590 panel.applicationMenus.hide();
591 }
592 }
593 onDrawerShownChanged: {
594 if (drawerShown) {
595 panel.indicators.hide();
596 panel.applicationMenus.hide();
597 }
598 }
599 onFocusChanged: {
600 if (!focus) {
601 stage.focus = true;
602 }
603 }
605 GlobalShortcut {
606 shortcut: Qt.MetaModifier | Qt.Key_A
607 onTriggered: {
608 launcher.openDrawer(true);
609 }
610 }
611 GlobalShortcut {
612 shortcut: Qt.AltModifier | Qt.Key_F1
613 onTriggered: {
614 launcher.openForKeyboardNavigation();
615 }
616 }
617 GlobalShortcut {
618 shortcut: Qt.MetaModifier | Qt.Key_0
619 onTriggered: {
620 if (LauncherModel.get(9)) {
621 activateApplication(LauncherModel.get(9).appId);
622 }
623 }
624 }
625 Repeater {
626 model: 9
627 GlobalShortcut {
628 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
629 onTriggered: {
630 if (LauncherModel.get(index)) {
631 activateApplication(LauncherModel.get(index).appId);
632 }
633 }
634 }
635 }
636 }
638 KeyboardShortcutsOverlay {
639 objectName: "shortcutsOverlay"
640 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
641 && height < parent.height - padding - panel.panelHeight
642 anchors.centerIn: parent
643 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
644 anchors.verticalCenterOffset: panel.panelHeight/2
645 visible: opacity > 0
646 opacity: enabled ? 0.95 : 0
648 Behavior on opacity {
649 UbuntuNumberAnimation {}
650 }
651 }
653 Tutorial {
654 id: tutorial
655 objectName: "tutorial"
656 anchors.fill: parent
658 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
659 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
660 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
661 inputMethod.visible ||
662 (launcher.shown && !launcher.lockedVisible) ||
663 panel.indicators.shown || stage.rightEdgeDragProgress > 0
664 usageScenario: shell.usageScenario
665 lastInputTimestamp: inputFilter.lastInputTimestamp
666 launcher: launcher
667 panel: panel
668 stage: stage
669 }
671 Wizard {
672 id: wizard
673 objectName: "wizard"
674 anchors.fill: parent
675 deferred: shell.mode === "greeter"
677 function unlockWhenDoneWithWizard() {
678 if (!active) {
679 Connectivity.unlockAllModems();
680 }
681 }
683 Component.onCompleted: unlockWhenDoneWithWizard()
684 onActiveChanged: unlockWhenDoneWithWizard()
685 }
687 MouseArea { // modal notifications prevent interacting with other contents
688 anchors.fill: parent
689 visible: notifications.useModal
690 enabled: visible
691 }
693 Notifications {
694 id: notifications
696 model: NotificationBackend.Model
697 margin: units.gu(1)
698 hasMouse: shell.hasMouse
699 background: wallpaperResolver.cachedBackground
701 y: topmostIsFullscreen ? 0 : panel.panelHeight
702 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
704 states: [
705 State {
706 name: "narrow"
707 when: overlay.width <= units.gu(60)
708 AnchorChanges {
709 target: notifications
710 anchors.left: parent.left
711 anchors.right: parent.right
712 }
713 },
714 State {
715 name: "wide"
716 when: overlay.width > units.gu(60)
717 AnchorChanges {
718 target: notifications
719 anchors.left: undefined
720 anchors.right: parent.right
721 }
722 PropertyChanges { target: notifications; width: units.gu(38) }
723 }
724 ]
725 }
727 EdgeBarrier {
728 id: rightEdgeBarrier
729 enabled: !greeter.shown
731 // NB: it does its own positioning according to the specified edge
732 edge: Qt.RightEdge
734 onPassed: {
735 panel.indicators.hide()
736 }
738 material: Component {
739 Item {
740 Rectangle {
741 width: parent.height
742 height: parent.width
743 rotation: 90
744 anchors.centerIn: parent
745 gradient: Gradient {
746 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
747 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
748 }
749 }
750 }
751 }
752 }
753 }
755 Dialogs {
756 id: dialogs
757 objectName: "dialogs"
758 anchors.fill: parent
759 visible: hasActiveDialog
760 z: overlay.z + 10
761 usageScenario: shell.usageScenario
762 hasKeyboard: shell.hasKeyboard
763 onPowerOffClicked: {
764 shutdownFadeOutRectangle.enabled = true;
765 shutdownFadeOutRectangle.visible = true;
766 shutdownFadeOut.start();
767 }
768 }
770 Connections {
771 target: SessionBroadcast
772 onShowHome: if (shell.mode !== "greeter") showHome()
773 }
775 URLDispatcher {
776 id: urlDispatcher
777 objectName: "urlDispatcher"
778 active: shell.mode === "greeter"
779 onUrlRequested: shell.activateURL(url)
780 }
782 ItemGrabber {
783 id: itemGrabber
784 anchors.fill: parent
785 z: dialogs.z + 10
786 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
787 Connections {
788 target: stage
789 ignoreUnknownSignals: true
790 onItemSnapshotRequested: itemGrabber.capture(item)
791 }
792 }
794 Timer {
795 id: cursorHidingTimer
796 interval: 3000
797 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
798 onTriggered: cursor.opacity = 0;
799 }
801 Cursor {
802 id: cursor
803 objectName: "cursor"
805 z: itemGrabber.z + 1
806 topBoundaryOffset: panel.panelHeight
807 enabled: shell.hasMouse && screenWindow.active
808 visible: enabled
810 property bool mouseNeverMoved: true
811 Binding {
812 target: cursor; property: "x"; value: shell.width / 2
813 when: cursor.mouseNeverMoved && cursor.visible
814 }
815 Binding {
816 target: cursor; property: "y"; value: shell.height / 2
817 when: cursor.mouseNeverMoved && cursor.visible
818 }
820 confiningItem: stage.itemConfiningMouseCursor
822 height: units.gu(3)
824 readonly property var previewRectangle: stage.previewRectangle.target &&
825 stage.previewRectangle.target.dragging ?
826 stage.previewRectangle : null
828 onPushedLeftBoundary: {
829 if (buttons === Qt.NoButton) {
830 launcher.pushEdge(amount);
831 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
832 previewRectangle.maximizeLeft(amount);
833 }
834 }
836 onPushedRightBoundary: {
837 if (buttons === Qt.NoButton) {
838 rightEdgeBarrier.push(amount);
839 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
840 previewRectangle.maximizeRight(amount);
841 }
842 }
844 onPushedTopBoundary: {
845 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
846 previewRectangle.maximize(amount);
847 }
848 }
849 onPushedTopLeftCorner: {
850 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
851 previewRectangle.maximizeTopLeft(amount);
852 }
853 }
854 onPushedTopRightCorner: {
855 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
856 previewRectangle.maximizeTopRight(amount);
857 }
858 }
859 onPushedBottomLeftCorner: {
860 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
861 previewRectangle.maximizeBottomLeft(amount);
862 }
863 }
864 onPushedBottomRightCorner: {
865 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
866 previewRectangle.maximizeBottomRight(amount);
867 }
868 }
869 onPushStopped: {
870 if (previewRectangle) {
871 previewRectangle.stop();
872 }
873 }
875 onMouseMoved: {
876 mouseNeverMoved = false;
877 cursor.opacity = 1;
878 }
880 Behavior on opacity { UbuntuNumberAnimation {} }
881 }
883 // non-visual objects
884 KeymapSwitcher {
885 focusedSurface: shell.topLevelSurfaceList.focusedWindow ? shell.topLevelSurfaceList.focusedWindow.surface : null
886 }
887 BrightnessControl {}
889 Rectangle {
890 id: shutdownFadeOutRectangle
891 z: cursor.z + 1
892 enabled: false
893 visible: false
894 color: "black"
895 anchors.fill: parent
896 opacity: 0.0
897 NumberAnimation on opacity {
898 id: shutdownFadeOut
899 from: 0.0
900 to: 1.0
901 onStopped: {
902 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
903 DBusUnitySessionService.shutdown();
904 }
905 }
906 }
907 }