on 32a567e help sign in
unity8/qml/Shell.qml
920 lines 33 kB history raw
1/*
2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 * Copyright (C) 2019-2020 UBports Foundation
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
18import QtQuick 2.4
19import QtQuick.Window 2.2
20import AccountsService 0.1
21import Unity.Application 0.1
22import Ubuntu.Components 1.3
23import Ubuntu.Components.Popups 1.3
24import Ubuntu.Gestures 0.1
25import Ubuntu.Telephony 0.1 as Telephony
26import Unity.Connectivity 0.1
27import Unity.Launcher 0.1
28import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
29import GSettings 1.0
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
72 property bool supportsMultiColorLed: true
74 // to be read from outside
75 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
77 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
78 && stage.orientationChangesEnabled
79 && (!greeter || !greeter.animating)
81 readonly property bool showingGreeter: greeter && greeter.shown
83 property bool startingUp: true
84 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
86 property int supportedOrientations: {
87 if (startingUp) {
88 // Ensure we don't rotate during start up
89 return Qt.PrimaryOrientation;
90 } else if (showingGreeter || notifications.topmostIsFullscreen) {
91 return Qt.PrimaryOrientation;
92 } else {
93 return shell.orientations ? shell.orientations.map(stage.supportedOrientations) : Qt.PrimaryOrientation;
94 }
95 }
97 readonly property var mainApp: stage.mainApp
99 onMainAppChanged: {
100 _onMainAppChanged((mainApp ? mainApp.appId : ""));
101 }
102 Connections {
103 target: ApplicationManager
104 onFocusRequested: {
105 if (shell.mainApp && shell.mainApp.appId === appId) {
106 _onMainAppChanged(appId);
107 }
108 }
109 }
111 // Calls attention back to the most important thing that's been focused
112 // (ex: phone calls go over Wizard, app focuses go over indicators, greeter
113 // goes over everything if it is locked)
114 // Must be called whenever app focus changes occur, even if the focus change
115 // is "nothing is focused". In that case, call with appId = ""
116 function _onMainAppChanged(appId) {
118 if (appId !== "") {
119 if (wizard.active) {
120 // If this happens on first boot, we may be in the
121 // wizard while receiving a call. 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. The
128 // Greeter will show it when it's notified of the focus.
129 // This can happen if user backs out of dialer back to greeter, then
130 // launches dialer again.
131 greeter.lockedApp = appId;
132 }
134 panel.indicators.hide();
135 launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
136 }
138 // *Always* make sure the greeter knows that the focused app changed
139 if (greeter) greeter.notifyAppFocusRequested(appId);
140 }
142 // For autopilot consumption
143 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
145 // Note when greeter is waiting on PAM, so that we can disable edges until
146 // we know which user data to show and whether the session is locked.
147 readonly property bool waitingOnGreeter: greeter && greeter.waiting
149 // True when the user is logged in with no apps running
150 readonly property bool atDesktop: topLevelSurfaceList && greeter && topLevelSurfaceList.count === 0 && !greeter.active
152 onAtDesktopChanged: {
153 if (atDesktop && stage) {
154 stage.closeSpread();
155 }
156 }
158 property real edgeSize: units.gu(settings.edgeDragWidth)
160 WallpaperResolver {
161 id: wallpaperResolver
162 objectName: "wallpaperResolver"
164 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
165 readonly property bool hasCustomBackground: background != defaultBackground
167 GSettings {
168 id: backgroundSettings
169 schema.id: "org.gnome.desktop.background"
170 }
172 candidates: [
173 AccountsService.backgroundFile,
174 backgroundSettings.pictureUri,
175 defaultBackground
176 ]
177 }
179 readonly property alias greeter: greeterLoader.item
181 function activateApplication(appId) {
182 topLevelSurfaceList.pendingActivation();
184 // Either open the app in our own session, or -- if we're acting as a
185 // greeter -- ask the user's session to open it for us.
186 if (shell.mode === "greeter") {
187 activateURL("application:///" + appId + ".desktop");
188 } else {
189 startApp(appId);
190 }
191 stage.focus = true;
192 }
194 function activateURL(url) {
195 SessionBroadcast.requestUrlStart(AccountsService.user, url);
196 greeter.notifyUserRequestedApp();
197 panel.indicators.hide();
198 }
200 function startApp(appId) {
201 if (ApplicationManager.findApplication(appId)) {
202 ApplicationManager.requestFocusApplication(appId);
203 } else {
204 ApplicationManager.startApplication(appId);
205 }
206 }
208 function startLockedApp(app) {
209 topLevelSurfaceList.pendingActivation();
211 if (greeter.locked) {
212 greeter.lockedApp = app;
213 }
214 startApp(app); // locked apps are always in our same session
215 }
217 Binding {
218 target: LauncherModel
219 property: "applicationManager"
220 value: ApplicationManager
221 }
223 Component.onCompleted: {
224 finishStartUpTimer.start();
225 }
227 VolumeControl {
228 id: volumeControl
229 }
231 PhysicalKeysMapper {
232 id: physicalKeysMapper
233 objectName: "physicalKeysMapper"
235 onPowerKeyLongPressed: dialogs.showPowerDialog();
236 onVolumeDownTriggered: volumeControl.volumeDown();
237 onVolumeUpTriggered: volumeControl.volumeUp();
238 onScreenshotTriggered: itemGrabber.capture(shell);
239 }
241 GlobalShortcut {
242 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
243 }
245 WindowInputFilter {
246 id: inputFilter
247 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
248 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
249 }
251 WindowInputMonitor {
252 objectName: "windowInputMonitor"
253 onHomeKeyActivated: {
254 // Ignore when greeter is active, to avoid pocket presses
255 if (!greeter.active) {
256 launcher.toggleDrawer(/* focusInputField */ false,
257 /* onlyOpen */ false,
258 /* alsoToggleLauncher */ true);
259 }
260 }
261 onTouchBegun: { cursor.opacity = 0; }
262 onTouchEnded: {
263 // move the (hidden) cursor to the last known touch position
264 var mappedCoords = mapFromItem(null, pos.x, pos.y);
265 cursor.x = mappedCoords.x;
266 cursor.y = mappedCoords.y;
267 cursor.mouseNeverMoved = false;
268 }
269 }
271 AvailableDesktopArea {
272 id: availableDesktopAreaItem
273 anchors.fill: parent
274 anchors.topMargin: panel.fullscreenMode ? 0 : panel.minimizedPanelHeight
275 anchors.leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
276 }
278 GSettings {
279 id: settings
280 schema.id: "com.canonical.Unity8"
281 }
283 Item {
284 id: stages
285 objectName: "stages"
286 width: parent.width
287 height: parent.height
289 SurfaceManager {
290 id: surfaceMan
291 objectName: "surfaceManager"
292 }
293 TopLevelWindowModel {
294 id: topLevelSurfaceList
295 objectName: "topLevelSurfaceList"
296 applicationManager: ApplicationManager // it's a singleton
297 surfaceManager: surfaceMan
298 }
300 Stage {
301 id: stage
302 objectName: "stage"
303 anchors.fill: parent
304 focus: true
306 dragAreaWidth: shell.edgeSize
307 background: wallpaperResolver.background
309 applicationManager: ApplicationManager
310 topLevelSurfaceList: topLevelSurfaceList
311 inputMethodRect: inputMethod.visibleRect
312 rightEdgePushProgress: rightEdgeBarrier.progress
313 availableDesktopArea: availableDesktopAreaItem
315 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
316 ? "phone"
317 : shell.usageScenario
319 mode: usageScenario == "phone" ? "staged"
320 : usageScenario == "tablet" ? "stagedWithSideStage"
321 : "windowed"
323 shellOrientation: shell.orientation
324 shellOrientationAngle: shell.orientationAngle
325 orientations: shell.orientations
326 nativeWidth: shell.nativeWidth
327 nativeHeight: shell.nativeHeight
329 allowInteractivity: (!greeter || !greeter.shown)
330 && panel.indicators.fullyClosed
331 && !notifications.useModal
332 && !launcher.takesFocus
334 suspended: greeter.shown
335 altTabPressed: physicalKeysMapper.altTabPressed
336 oskEnabled: shell.oskEnabled
337 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
339 onSpreadShownChanged: {
340 panel.indicators.hide();
341 panel.applicationMenus.hide();
342 }
343 }
345 TouchGestureArea {
346 anchors.fill: stage
348 minimumTouchPoints: 4
349 maximumTouchPoints: minimumTouchPoints
351 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
352 touchPoints.length >= minimumTouchPoints &&
353 touchPoints.length <= maximumTouchPoints
354 property bool wasPressed: false
356 onRecognisedPressChanged: {
357 if (recognisedPress) {
358 wasPressed = true;
359 }
360 }
362 onStatusChanged: {
363 if (status !== TouchGestureArea.Recognized) {
364 if (status === TouchGestureArea.WaitingForTouch) {
365 if (wasPressed && !dragging) {
366 launcher.toggleDrawer(true);
367 }
368 }
369 wasPressed = false;
370 }
371 }
372 }
373 }
375 InputMethod {
376 id: inputMethod
377 objectName: "inputMethod"
378 anchors {
379 fill: parent
380 topMargin: panel.panelHeight
381 leftMargin: (launcher.lockedByUser && launcher.lockAllowed) ? launcher.panelWidth : 0
382 }
383 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
384 }
386 Loader {
387 id: greeterLoader
388 objectName: "greeterLoader"
389 anchors.fill: parent
390 anchors.topMargin: panel.panelHeight
391 sourceComponent: shell.mode != "shell" ? integratedGreeter :
392 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
393 onLoaded: {
394 item.objectName = "greeter"
395 }
396 property bool toggleDrawerAfterUnlock: false
397 Connections {
398 target: greeter
399 onActiveChanged: {
400 if (greeter.active)
401 return
403 // Show drawer in case showHome() requests it
404 if (greeterLoader.toggleDrawerAfterUnlock) {
405 launcher.toggleDrawer(false);
406 greeterLoader.toggleDrawerAfterUnlock = false;
407 } else {
408 launcher.hide();
409 }
410 }
411 }
412 }
414 Component {
415 id: integratedGreeter
416 Greeter {
418 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
419 hides: [launcher, panel.indicators, panel.applicationMenus]
420 tabletMode: shell.usageScenario != "phone"
421 forcedUnlock: wizard.active || shell.mode === "full-shell"
422 background: wallpaperResolver.background
423 hasCustomBackground: wallpaperResolver.hasCustomBackground
424 allowFingerprint: !dialogs.hasActiveDialog &&
425 !notifications.topmostIsFullscreen &&
426 !panel.indicators.shown
428 // avoid overlapping with Launcher's edge drag area
429 // FIXME: Fix TouchRegistry & friends and remove this workaround
430 // Issue involves launcher's DDA getting disabled on a long
431 // left-edge drag
432 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
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 applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
525 indicators {
526 hides: [launcher]
527 available: tutorial.panelEnabled
528 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
529 && (!greeter || !greeter.hasLockedApp)
530 && !shell.waitingOnGreeter
531 && settings.enableIndicatorMenu
533 model: Indicators.IndicatorsModel {
534 // tablet and phone both use the same profile
535 // FIXME: use just "phone" for greeter too, but first fix
536 // greeter app launching to either load the app inside the
537 // greeter or tell the session to load the app. This will
538 // involve taking the url-dispatcher dbus name and using
539 // SessionBroadcast to tell the session.
540 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
541 Component.onCompleted: load();
542 }
543 }
545 applicationMenus {
546 hides: [launcher]
547 available: (!greeter || !greeter.shown)
548 && !shell.waitingOnGreeter
549 && !stage.spreadShown
550 }
552 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
553 ? topLevelSurfaceList.focusedWindow.state == Mir.FullscreenState
554 : false
555 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
556 || greeter.hasLockedApp
557 greeterShown: greeter && greeter.shown
558 hasKeyboard: shell.hasKeyboard
559 supportsMultiColorLed: shell.supportsMultiColorLed
560 }
562 Launcher {
563 id: launcher
564 objectName: "launcher"
566 anchors.top: parent.top
567 anchors.topMargin: inverted ? 0 : panel.panelHeight
568 anchors.bottom: parent.bottom
569 width: parent.width
570 dragAreaWidth: shell.edgeSize
571 available: tutorial.launcherEnabled
572 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
573 && !greeter.hasLockedApp
574 && !shell.waitingOnGreeter
575 inverted: shell.usageScenario !== "desktop"
576 superPressed: physicalKeysMapper.superPressed
577 superTabPressed: physicalKeysMapper.superTabPressed
578 panelWidth: units.gu(settings.launcherWidth)
579 lockedVisible: (lockedByUser || shell.atDesktop) && lockAllowed
580 topPanelHeight: panel.panelHeight
581 drawerEnabled: !greeter.active && tutorial.launcherLongSwipeEnabled
582 privateMode: greeter.active
583 background: wallpaperResolver.background
585 // It can be assumed that the Launcher and Panel would overlap if
586 // the Panel is open and taking up the full width of the shell
587 readonly property bool collidingWithPanel: panel && (!panel.fullyClosed && !panel.partialWidth)
589 // The "autohideLauncher" setting is only valid in desktop mode
590 readonly property bool lockedByUser: (shell.usageScenario == "desktop" && !settings.autohideLauncher)
592 // The Launcher should absolutely not be locked visible under some
593 // conditions
594 readonly property bool lockAllowed: !collidingWithPanel && !panel.fullscreenMode && !wizard.active && !tutorial.demonstrateLauncher
596 onShowDashHome: showHome()
597 onLauncherApplicationSelected: {
598 greeter.notifyUserRequestedApp();
599 shell.activateApplication(appId);
600 }
601 onShownChanged: {
602 if (shown) {
603 panel.indicators.hide();
604 panel.applicationMenus.hide();
605 }
606 }
607 onDrawerShownChanged: {
608 if (drawerShown) {
609 panel.indicators.hide();
610 panel.applicationMenus.hide();
611 }
612 }
613 onFocusChanged: {
614 if (!focus) {
615 stage.focus = true;
616 }
617 }
619 GlobalShortcut {
620 shortcut: Qt.MetaModifier | Qt.Key_A
621 onTriggered: {
622 launcher.toggleDrawer(true);
623 }
624 }
625 GlobalShortcut {
626 shortcut: Qt.AltModifier | Qt.Key_F1
627 onTriggered: {
628 launcher.openForKeyboardNavigation();
629 }
630 }
631 GlobalShortcut {
632 shortcut: Qt.MetaModifier | Qt.Key_0
633 onTriggered: {
634 if (LauncherModel.get(9)) {
635 activateApplication(LauncherModel.get(9).appId);
636 }
637 }
638 }
639 Repeater {
640 model: 9
641 GlobalShortcut {
642 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
643 onTriggered: {
644 if (LauncherModel.get(index)) {
645 activateApplication(LauncherModel.get(index).appId);
646 }
647 }
648 }
649 }
650 }
652 KeyboardShortcutsOverlay {
653 objectName: "shortcutsOverlay"
654 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
655 && height < parent.height - padding - panel.panelHeight
656 anchors.centerIn: parent
657 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
658 anchors.verticalCenterOffset: panel.panelHeight/2
659 visible: opacity > 0
660 opacity: enabled ? 0.95 : 0
662 Behavior on opacity {
663 UbuntuNumberAnimation {}
664 }
665 }
667 Tutorial {
668 id: tutorial
669 objectName: "tutorial"
670 anchors.fill: parent
672 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
673 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
674 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
675 inputMethod.visible ||
676 (launcher.shown && !launcher.lockedVisible) ||
677 panel.indicators.shown || stage.rightEdgeDragProgress > 0
678 usageScenario: shell.usageScenario
679 lastInputTimestamp: inputFilter.lastInputTimestamp
680 launcher: launcher
681 panel: panel
682 stage: stage
683 }
685 Wizard {
686 id: wizard
687 objectName: "wizard"
688 anchors.fill: parent
689 deferred: shell.mode === "greeter"
691 function unlockWhenDoneWithWizard() {
692 if (!active) {
693 Connectivity.unlockAllModems();
694 }
695 }
697 Component.onCompleted: unlockWhenDoneWithWizard()
698 onActiveChanged: unlockWhenDoneWithWizard()
699 }
701 MouseArea { // modal notifications prevent interacting with other contents
702 anchors.fill: parent
703 visible: notifications.useModal
704 enabled: visible
705 }
707 Notifications {
708 id: notifications
710 model: NotificationBackend.Model
711 margin: units.gu(1)
712 hasMouse: shell.hasMouse
713 background: wallpaperResolver.background
715 y: topmostIsFullscreen ? 0 : panel.panelHeight
716 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
718 states: [
719 State {
720 name: "narrow"
721 when: overlay.width <= units.gu(60)
722 AnchorChanges {
723 target: notifications
724 anchors.left: parent.left
725 anchors.right: parent.right
726 }
727 },
728 State {
729 name: "wide"
730 when: overlay.width > units.gu(60)
731 AnchorChanges {
732 target: notifications
733 anchors.left: undefined
734 anchors.right: parent.right
735 }
736 PropertyChanges { target: notifications; width: units.gu(38) }
737 }
738 ]
739 }
741 EdgeBarrier {
742 id: rightEdgeBarrier
743 enabled: !greeter.shown
745 // NB: it does its own positioning according to the specified edge
746 edge: Qt.RightEdge
748 onPassed: {
749 panel.indicators.hide()
750 }
752 material: Component {
753 Item {
754 Rectangle {
755 width: parent.height
756 height: parent.width
757 rotation: 90
758 anchors.centerIn: parent
759 gradient: Gradient {
760 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
761 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
762 }
763 }
764 }
765 }
766 }
767 }
769 Dialogs {
770 id: dialogs
771 objectName: "dialogs"
772 anchors.fill: parent
773 visible: hasActiveDialog
774 z: overlay.z + 10
775 usageScenario: shell.usageScenario
776 hasKeyboard: shell.hasKeyboard
777 onPowerOffClicked: {
778 shutdownFadeOutRectangle.enabled = true;
779 shutdownFadeOutRectangle.visible = true;
780 shutdownFadeOut.start();
781 }
782 }
784 Connections {
785 target: SessionBroadcast
786 onShowHome: if (shell.mode !== "greeter") showHome()
787 }
789 URLDispatcher {
790 id: urlDispatcher
791 objectName: "urlDispatcher"
792 active: shell.mode === "greeter"
793 onUrlRequested: shell.activateURL(url)
794 }
796 ItemGrabber {
797 id: itemGrabber
798 anchors.fill: parent
799 z: dialogs.z + 10
800 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
801 Connections {
802 target: stage
803 ignoreUnknownSignals: true
804 onItemSnapshotRequested: itemGrabber.capture(item)
805 }
806 }
808 Timer {
809 id: cursorHidingTimer
810 interval: 3000
811 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
812 onTriggered: cursor.opacity = 0;
813 }
815 Cursor {
816 id: cursor
817 objectName: "cursor"
818 visible: shell.hasMouse
819 z: itemGrabber.z + 1
820 topBoundaryOffset: panel.panelHeight
822 confiningItem: stage.itemConfiningMouseCursor
824 property bool mouseNeverMoved: true
825 Binding {
826 target: cursor; property: "x"; value: shell.width / 2
827 when: cursor.mouseNeverMoved && cursor.visible
828 }
829 Binding {
830 target: cursor; property: "y"; value: shell.height / 2
831 when: cursor.mouseNeverMoved && cursor.visible
832 }
834 height: units.gu(3)
836 readonly property var previewRectangle: stage.previewRectangle.target &&
837 stage.previewRectangle.target.dragging ?
838 stage.previewRectangle : null
840 onPushedLeftBoundary: {
841 if (buttons === Qt.NoButton) {
842 launcher.pushEdge(amount);
843 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
844 previewRectangle.maximizeLeft(amount);
845 }
846 }
848 onPushedRightBoundary: {
849 if (buttons === Qt.NoButton) {
850 rightEdgeBarrier.push(amount);
851 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
852 previewRectangle.maximizeRight(amount);
853 }
854 }
856 onPushedTopBoundary: {
857 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
858 previewRectangle.maximize(amount);
859 }
860 }
861 onPushedTopLeftCorner: {
862 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
863 previewRectangle.maximizeTopLeft(amount);
864 }
865 }
866 onPushedTopRightCorner: {
867 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
868 previewRectangle.maximizeTopRight(amount);
869 }
870 }
871 onPushedBottomLeftCorner: {
872 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
873 previewRectangle.maximizeBottomLeft(amount);
874 }
875 }
876 onPushedBottomRightCorner: {
877 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
878 previewRectangle.maximizeBottomRight(amount);
879 }
880 }
881 onPushStopped: {
882 if (previewRectangle) {
883 previewRectangle.stop();
884 }
885 }
887 onMouseMoved: {
888 mouseNeverMoved = false;
889 cursor.opacity = 1;
890 }
892 Behavior on opacity { UbuntuNumberAnimation {} }
893 }
895 // non-visual objects
896 KeymapSwitcher {
897 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
898 }
899 BrightnessControl {}
901 Rectangle {
902 id: shutdownFadeOutRectangle
903 z: cursor.z + 1
904 enabled: false
905 visible: false
906 color: "black"
907 anchors.fill: parent
908 opacity: 0.0
909 NumberAnimation on opacity {
910 id: shutdownFadeOut
911 from: 0.0
912 to: 1.0
913 onStopped: {
914 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
915 DBusUnitySessionService.shutdown();
916 }
917 }
918 }
919 }