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