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