on 2adba16 help sign in
unity8/qml/Shell.qml
914 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 ApplicationManager.startApplication(appId);
196 }
197 }
199 function startLockedApp(app) {
200 if (greeter.locked) {
201 greeter.lockedApp = app;
202 }
203 startApp(app); // locked apps are always in our same session
204 }
206 Binding {
207 target: LauncherModel
208 property: "applicationManager"
209 value: ApplicationManager
210 }
212 Component.onCompleted: {
213 finishStartUpTimer.start();
214 }
216 VolumeControl {
217 id: volumeControl
218 }
220 DashCommunicator {
221 id: dash
222 objectName: "dashCommunicator"
223 }
225 PhysicalKeysMapper {
226 id: physicalKeysMapper
227 objectName: "physicalKeysMapper"
229 onPowerKeyLongPressed: dialogs.showPowerDialog();
230 onVolumeDownTriggered: volumeControl.volumeDown();
231 onVolumeUpTriggered: volumeControl.volumeUp();
232 onScreenshotTriggered: itemGrabber.capture(shell);
233 }
235 GlobalShortcut {
236 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
237 }
239 WindowInputFilter {
240 id: inputFilter
241 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
242 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
243 }
245 WindowInputMonitor {
246 objectName: "windowInputMonitor"
247 onHomeKeyActivated: {
248 // Ignore when greeter is active, to avoid pocket presses
249 if (!greeter.active) {
250 launcher.fadeOut();
251 ApplicationManager.requestFocusApplication("unity8-dash");
252 }
253 }
254 onTouchBegun: { cursor.opacity = 0; }
255 onTouchEnded: {
256 // move the (hidden) cursor to the last known touch position
257 var mappedCoords = mapFromItem(null, pos.x, pos.y);
258 cursor.x = mappedCoords.x;
259 cursor.y = mappedCoords.y;
260 cursor.mouseNeverMoved = false;
261 }
262 }
264 AvailableDesktopArea {
265 id: availableDesktopAreaItem
266 anchors.fill: parent
267 anchors.topMargin: panel.fullscreenMode ? 0 : panel.minimizedPanelHeight
268 anchors.leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
269 }
271 GSettings {
272 id: settings
273 schema.id: "com.canonical.Unity8"
274 }
276 PanelState {
277 id: panelState
278 objectName: "panelState"
279 }
281 Item {
282 id: stages
283 objectName: "stages"
284 width: parent.width
285 height: parent.height
287 Stage {
288 id: stage
289 objectName: "stage"
290 anchors.fill: parent
291 focus: true
293 dragAreaWidth: shell.edgeSize
294 background: wallpaperResolver.background
296 applicationManager: ApplicationManager
297 topLevelSurfaceList: shell.topLevelSurfaceList
298 inputMethodRect: inputMethod.visibleRect
299 rightEdgePushProgress: rightEdgeBarrier.progress
300 availableDesktopArea: availableDesktopAreaItem
302 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
303 ? "phone"
304 : shell.usageScenario
306 mode: usageScenario == "phone" ? "staged"
307 : usageScenario == "tablet" ? "stagedWithSideStage"
308 : "windowed"
310 shellOrientation: shell.orientation
311 shellOrientationAngle: shell.orientationAngle
312 orientations: shell.orientations
313 nativeWidth: shell.nativeWidth
314 nativeHeight: shell.nativeHeight
316 interactive: (!greeter || !greeter.shown)
317 && panel.indicators.fullyClosed
318 && !notifications.useModal
320 onInteractiveChanged: { if (interactive) { focus = true; } }
322 suspended: greeter.shown
323 altTabPressed: physicalKeysMapper.altTabPressed
324 oskEnabled: shell.oskEnabled
325 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
326 panelState: panelState
328 onSpreadShownChanged: {
329 panel.indicators.hide();
330 panel.applicationMenus.hide();
331 }
332 }
334 TouchGestureArea {
335 anchors.fill: stage
337 minimumTouchPoints: 4
338 maximumTouchPoints: minimumTouchPoints
340 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
341 touchPoints.length >= minimumTouchPoints &&
342 touchPoints.length <= maximumTouchPoints
343 property bool wasPressed: false
345 onRecognisedPressChanged: {
346 if (recognisedPress) {
347 wasPressed = true;
348 }
349 }
351 onStatusChanged: {
352 if (status !== TouchGestureArea.Recognized) {
353 if (status === TouchGestureArea.WaitingForTouch) {
354 if (wasPressed && !dragging) {
355 launcher.openDrawer(true);
356 }
357 }
358 wasPressed = false;
359 }
360 }
361 }
362 }
364 InputMethod {
365 id: inputMethod
366 objectName: "inputMethod"
367 surface: shell.topLevelSurfaceList.inputMethodSurface
368 anchors {
369 fill: parent
370 topMargin: panel.panelHeight
371 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
372 }
373 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
374 }
376 Loader {
377 id: greeterLoader
378 objectName: "greeterLoader"
379 anchors.fill: parent
380 anchors.topMargin: panel.panelHeight
381 sourceComponent: {
382 if (shell.mode != "shell") {
383 if (screenWindow.primary) return integratedGreeter;
384 return secondaryGreeter;
385 }
386 return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
387 }
388 onLoaded: {
389 item.objectName = "greeter"
390 }
391 property bool openDrawerAfterUnlock: false
392 Connections {
393 target: greeter
394 onActiveChanged: {
395 if (!greeter.active && greeterLoader.openDrawerAfterUnlock) {
396 launcher.openDrawer(false);
397 greeterLoader.openDrawerAfterUnlock = false;
398 }
399 }
400 }
401 }
403 Component {
404 id: integratedGreeter
405 Greeter {
407 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
408 hides: [launcher, panel.indicators, panel.applicationMenus]
409 tabletMode: shell.usageScenario != "phone"
410 forcedUnlock: wizard.active || shell.mode === "full-shell"
411 background: wallpaperResolver.cachedBackground
412 hasCustomBackground: wallpaperResolver.hasCustomBackground
413 allowFingerprint: !dialogs.hasActiveDialog &&
414 !notifications.topmostIsFullscreen &&
415 !panel.indicators.shown
417 // avoid overlapping with Launcher's edge drag area
418 // FIXME: Fix TouchRegistry & friends and remove this workaround
419 // Issue involves launcher's DDA getting disabled on a long
420 // left-edge drag
421 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
423 onSessionStarted: {
424 launcher.hide();
425 }
427 onTease: {
428 if (!tutorial.running) {
429 launcher.tease();
430 }
431 }
433 onEmergencyCall: startLockedApp("dialer-app")
434 }
435 }
437 Component {
438 id: secondaryGreeter
439 SecondaryGreeter {
440 hides: [launcher, panel.indicators]
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.openDrawer(false);
503 } else {
504 greeterLoader.openDrawerAfterUnlock = 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: shell.topLevelSurfaceList.focusedWindow
555 ? shell.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 panelState: panelState
562 }
564 Launcher {
565 id: launcher
566 objectName: "launcher"
568 anchors.top: parent.top
569 anchors.topMargin: inverted ? 0 : panel.panelHeight
570 anchors.bottom: parent.bottom
571 width: parent.width
572 dragAreaWidth: shell.edgeSize
573 available: tutorial.launcherEnabled
574 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
575 && !greeter.hasLockedApp
576 && !shell.waitingOnGreeter
577 && settings.enableLauncher
578 inverted: shell.usageScenario !== "desktop"
579 superPressed: physicalKeysMapper.superPressed
580 superTabPressed: physicalKeysMapper.superTabPressed
581 panelWidth: units.gu(settings.launcherWidth)
582 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
583 blurSource: greeter.shown ? greeter : stages
584 topPanelHeight: panel.panelHeight
585 drawerEnabled: !greeter.active
586 privateMode: greeter.active
588 onShowDashHome: showHome()
589 onLauncherApplicationSelected: {
590 greeter.notifyUserRequestedApp();
591 shell.activateApplication(appId);
592 }
593 onShownChanged: {
594 if (shown) {
595 panel.indicators.hide();
596 panel.applicationMenus.hide();
597 }
598 }
599 onDrawerShownChanged: {
600 if (drawerShown) {
601 panel.indicators.hide();
602 panel.applicationMenus.hide();
603 }
604 }
605 onFocusChanged: {
606 if (!focus) {
607 stage.focus = true;
608 }
609 }
611 GlobalShortcut {
612 shortcut: Qt.MetaModifier | Qt.Key_A
613 onTriggered: {
614 launcher.openDrawer(true);
615 }
616 }
617 GlobalShortcut {
618 shortcut: Qt.AltModifier | Qt.Key_F1
619 onTriggered: {
620 launcher.openForKeyboardNavigation();
621 }
622 }
623 GlobalShortcut {
624 shortcut: Qt.MetaModifier | Qt.Key_0
625 onTriggered: {
626 if (LauncherModel.get(9)) {
627 activateApplication(LauncherModel.get(9).appId);
628 }
629 }
630 }
631 Repeater {
632 model: 9
633 GlobalShortcut {
634 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
635 onTriggered: {
636 if (LauncherModel.get(index)) {
637 activateApplication(LauncherModel.get(index).appId);
638 }
639 }
640 }
641 }
642 }
644 KeyboardShortcutsOverlay {
645 objectName: "shortcutsOverlay"
646 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
647 && height < parent.height - padding - panel.panelHeight
648 anchors.centerIn: parent
649 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
650 anchors.verticalCenterOffset: panel.panelHeight/2
651 visible: opacity > 0
652 opacity: enabled ? 0.95 : 0
654 Behavior on opacity {
655 UbuntuNumberAnimation {}
656 }
657 }
659 Tutorial {
660 id: tutorial
661 objectName: "tutorial"
662 anchors.fill: parent
664 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
665 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
666 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
667 inputMethod.visible ||
668 (launcher.shown && !launcher.lockedVisible) ||
669 panel.indicators.shown || stage.rightEdgeDragProgress > 0
670 usageScenario: shell.usageScenario
671 lastInputTimestamp: inputFilter.lastInputTimestamp
672 launcher: launcher
673 panel: panel
674 stage: stage
675 }
677 Wizard {
678 id: wizard
679 objectName: "wizard"
680 anchors.fill: parent
681 deferred: shell.mode === "greeter"
683 function unlockWhenDoneWithWizard() {
684 if (!active) {
685 Connectivity.unlockAllModems();
686 }
687 }
689 Component.onCompleted: unlockWhenDoneWithWizard()
690 onActiveChanged: unlockWhenDoneWithWizard()
691 }
693 MouseArea { // modal notifications prevent interacting with other contents
694 anchors.fill: parent
695 visible: notifications.useModal
696 enabled: visible
697 }
699 Notifications {
700 id: notifications
702 model: NotificationBackend.Model
703 margin: units.gu(1)
704 hasMouse: shell.hasMouse
705 background: wallpaperResolver.cachedBackground
707 y: topmostIsFullscreen ? 0 : panel.panelHeight
708 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
710 states: [
711 State {
712 name: "narrow"
713 when: overlay.width <= units.gu(60)
714 AnchorChanges {
715 target: notifications
716 anchors.left: parent.left
717 anchors.right: parent.right
718 }
719 },
720 State {
721 name: "wide"
722 when: overlay.width > units.gu(60)
723 AnchorChanges {
724 target: notifications
725 anchors.left: undefined
726 anchors.right: parent.right
727 }
728 PropertyChanges { target: notifications; width: units.gu(38) }
729 }
730 ]
731 }
733 EdgeBarrier {
734 id: rightEdgeBarrier
735 enabled: !greeter.shown
737 // NB: it does its own positioning according to the specified edge
738 edge: Qt.RightEdge
740 onPassed: {
741 panel.indicators.hide()
742 }
744 material: Component {
745 Item {
746 Rectangle {
747 width: parent.height
748 height: parent.width
749 rotation: 90
750 anchors.centerIn: parent
751 gradient: Gradient {
752 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
753 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
754 }
755 }
756 }
757 }
758 }
759 }
761 Dialogs {
762 id: dialogs
763 objectName: "dialogs"
764 anchors.fill: parent
765 visible: hasActiveDialog
766 z: overlay.z + 10
767 usageScenario: shell.usageScenario
768 hasKeyboard: shell.hasKeyboard
769 onPowerOffClicked: {
770 shutdownFadeOutRectangle.enabled = true;
771 shutdownFadeOutRectangle.visible = true;
772 shutdownFadeOut.start();
773 }
774 }
776 Connections {
777 target: SessionBroadcast
778 onShowHome: if (shell.mode !== "greeter") showHome()
779 }
781 URLDispatcher {
782 id: urlDispatcher
783 objectName: "urlDispatcher"
784 active: shell.mode === "greeter"
785 onUrlRequested: shell.activateURL(url)
786 }
788 ItemGrabber {
789 id: itemGrabber
790 anchors.fill: parent
791 z: dialogs.z + 10
792 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
793 Connections {
794 target: stage
795 ignoreUnknownSignals: true
796 onItemSnapshotRequested: itemGrabber.capture(item)
797 }
798 }
800 Timer {
801 id: cursorHidingTimer
802 interval: 3000
803 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
804 onTriggered: cursor.opacity = 0;
805 }
807 Cursor {
808 id: cursor
809 objectName: "cursor"
811 z: itemGrabber.z + 1
812 topBoundaryOffset: panel.panelHeight
813 enabled: shell.hasMouse && screenWindow.active
814 visible: enabled
816 property bool mouseNeverMoved: true
817 Binding {
818 target: cursor; property: "x"; value: shell.width / 2
819 when: cursor.mouseNeverMoved && cursor.visible
820 }
821 Binding {
822 target: cursor; property: "y"; value: shell.height / 2
823 when: cursor.mouseNeverMoved && cursor.visible
824 }
826 confiningItem: stage.itemConfiningMouseCursor
828 height: units.gu(3)
830 readonly property var previewRectangle: stage.previewRectangle.target &&
831 stage.previewRectangle.target.dragging ?
832 stage.previewRectangle : null
834 onPushedLeftBoundary: {
835 if (buttons === Qt.NoButton) {
836 launcher.pushEdge(amount);
837 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
838 previewRectangle.maximizeLeft(amount);
839 }
840 }
842 onPushedRightBoundary: {
843 if (buttons === Qt.NoButton) {
844 rightEdgeBarrier.push(amount);
845 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
846 previewRectangle.maximizeRight(amount);
847 }
848 }
850 onPushedTopBoundary: {
851 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
852 previewRectangle.maximize(amount);
853 }
854 }
855 onPushedTopLeftCorner: {
856 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
857 previewRectangle.maximizeTopLeft(amount);
858 }
859 }
860 onPushedTopRightCorner: {
861 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
862 previewRectangle.maximizeTopRight(amount);
863 }
864 }
865 onPushedBottomLeftCorner: {
866 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
867 previewRectangle.maximizeBottomLeft(amount);
868 }
869 }
870 onPushedBottomRightCorner: {
871 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
872 previewRectangle.maximizeBottomRight(amount);
873 }
874 }
875 onPushStopped: {
876 if (previewRectangle) {
877 previewRectangle.stop();
878 }
879 }
881 onMouseMoved: {
882 mouseNeverMoved = false;
883 cursor.opacity = 1;
884 }
886 Behavior on opacity { UbuntuNumberAnimation {} }
887 }
889 // non-visual objects
890 KeymapSwitcher {
891 focusedSurface: shell.topLevelSurfaceList.focusedWindow ? shell.topLevelSurfaceList.focusedWindow.surface : null
892 }
893 BrightnessControl {}
895 Rectangle {
896 id: shutdownFadeOutRectangle
897 z: cursor.z + 1
898 enabled: false
899 visible: false
900 color: "black"
901 anchors.fill: parent
902 opacity: 0.0
903 NumberAnimation on opacity {
904 id: shutdownFadeOut
905 from: 0.0
906 to: 1.0
907 onStopped: {
908 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
909 DBusUnitySessionService.shutdown();
910 }
911 }
912 }
913 }