on ac69b16 help sign in
unity8/qml/Shell.qml
921 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
314 launcherLeftMargin: launcher.visibleWidth
316 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
317 ? "phone"
318 : shell.usageScenario
320 mode: usageScenario == "phone" ? "staged"
321 : usageScenario == "tablet" ? "stagedWithSideStage"
322 : "windowed"
324 shellOrientation: shell.orientation
325 shellOrientationAngle: shell.orientationAngle
326 orientations: shell.orientations
327 nativeWidth: shell.nativeWidth
328 nativeHeight: shell.nativeHeight
330 allowInteractivity: (!greeter || !greeter.shown)
331 && panel.indicators.fullyClosed
332 && !notifications.useModal
333 && !launcher.takesFocus
335 suspended: greeter.shown
336 altTabPressed: physicalKeysMapper.altTabPressed
337 oskEnabled: shell.oskEnabled
338 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
340 onSpreadShownChanged: {
341 panel.indicators.hide();
342 panel.applicationMenus.hide();
343 }
344 }
346 TouchGestureArea {
347 anchors.fill: stage
349 minimumTouchPoints: 4
350 maximumTouchPoints: minimumTouchPoints
352 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
353 touchPoints.length >= minimumTouchPoints &&
354 touchPoints.length <= maximumTouchPoints
355 property bool wasPressed: false
357 onRecognisedPressChanged: {
358 if (recognisedPress) {
359 wasPressed = true;
360 }
361 }
363 onStatusChanged: {
364 if (status !== TouchGestureArea.Recognized) {
365 if (status === TouchGestureArea.WaitingForTouch) {
366 if (wasPressed && !dragging) {
367 launcher.toggleDrawer(true);
368 }
369 }
370 wasPressed = false;
371 }
372 }
373 }
374 }
376 InputMethod {
377 id: inputMethod
378 objectName: "inputMethod"
379 anchors {
380 fill: parent
381 topMargin: panel.panelHeight
382 leftMargin: (launcher.lockedByUser && launcher.lockAllowed) ? launcher.panelWidth : 0
383 }
384 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
385 }
387 Loader {
388 id: greeterLoader
389 objectName: "greeterLoader"
390 anchors.fill: parent
391 anchors.topMargin: panel.panelHeight
392 sourceComponent: shell.mode != "shell" ? integratedGreeter :
393 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
394 onLoaded: {
395 item.objectName = "greeter"
396 }
397 property bool toggleDrawerAfterUnlock: false
398 Connections {
399 target: greeter
400 onActiveChanged: {
401 if (greeter.active)
402 return
404 // Show drawer in case showHome() requests it
405 if (greeterLoader.toggleDrawerAfterUnlock) {
406 launcher.toggleDrawer(false);
407 greeterLoader.toggleDrawerAfterUnlock = false;
408 } else {
409 launcher.hide();
410 }
411 }
412 }
413 }
415 Component {
416 id: integratedGreeter
417 Greeter {
419 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
420 hides: [launcher, panel.indicators, panel.applicationMenus]
421 tabletMode: shell.usageScenario != "phone"
422 forcedUnlock: wizard.active || shell.mode === "full-shell"
423 background: wallpaperResolver.background
424 hasCustomBackground: wallpaperResolver.hasCustomBackground
425 allowFingerprint: !dialogs.hasActiveDialog &&
426 !notifications.topmostIsFullscreen &&
427 !panel.indicators.shown
429 // avoid overlapping with Launcher's edge drag area
430 // FIXME: Fix TouchRegistry & friends and remove this workaround
431 // Issue involves launcher's DDA getting disabled on a long
432 // left-edge drag
433 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
435 onTease: {
436 if (!tutorial.running) {
437 launcher.tease();
438 }
439 }
441 onEmergencyCall: startLockedApp("dialer-app")
442 }
443 }
445 Timer {
446 // See powerConnection for why this is useful
447 id: showGreeterDelayed
448 interval: 1
449 onTriggered: {
450 // Go through the dbus service, because it has checks for whether
451 // we are even allowed to lock or not.
452 DBusUnitySessionService.PromptLock();
453 }
454 }
456 Connections {
457 id: callConnection
458 target: callManager
460 onHasCallsChanged: {
461 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
462 // We just received an incoming call while locked. The
463 // indicator will have already launched dialer-app for us, but
464 // there is a race between "hasCalls" changing and the dialer
465 // starting up. So in case we lose that race, we'll start/
466 // focus the dialer ourselves here too. Even if the indicator
467 // didn't launch the dialer for some reason (or maybe a call
468 // started via some other means), if an active call is
469 // happening, we want to be in the dialer.
470 startLockedApp("dialer-app")
471 }
472 }
473 }
475 Connections {
476 id: powerConnection
477 target: Powerd
479 onStatusChanged: {
480 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
481 !callManager.hasCalls && !wizard.active) {
482 // We don't want to simply call greeter.showNow() here, because
483 // that will take too long. Qt will delay button event
484 // handling until the greeter is done loading and may think the
485 // user held down the power button the whole time, leading to a
486 // power dialog being shown. Instead, delay showing the
487 // greeter until we've finished handling the event. We could
488 // make the greeter load asynchronously instead, but that
489 // introduces a whole host of timing issues, especially with
490 // its animations. So this is simpler.
491 showGreeterDelayed.start();
492 }
493 }
494 }
496 function showHome() {
497 greeter.notifyUserRequestedApp();
499 if (shell.mode === "greeter") {
500 SessionBroadcast.requestHomeShown(AccountsService.user);
501 } else {
502 if (!greeter.active) {
503 launcher.toggleDrawer(false);
504 } else {
505 greeterLoader.toggleDrawerAfterUnlock = true;
506 }
507 }
508 }
510 Item {
511 id: overlay
512 z: 10
514 anchors.fill: parent
516 Panel {
517 id: panel
518 objectName: "panel"
519 anchors.fill: parent //because this draws indicator menus
521 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
522 minimizedPanelHeight: units.gu(3)
523 expandedPanelHeight: units.gu(7)
524 applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
526 indicators {
527 hides: [launcher]
528 available: tutorial.panelEnabled
529 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
530 && (!greeter || !greeter.hasLockedApp)
531 && !shell.waitingOnGreeter
532 && settings.enableIndicatorMenu
534 model: Indicators.IndicatorsModel {
535 // tablet and phone both use the same profile
536 // FIXME: use just "phone" for greeter too, but first fix
537 // greeter app launching to either load the app inside the
538 // greeter or tell the session to load the app. This will
539 // involve taking the url-dispatcher dbus name and using
540 // SessionBroadcast to tell the session.
541 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
542 Component.onCompleted: load();
543 }
544 }
546 applicationMenus {
547 hides: [launcher]
548 available: (!greeter || !greeter.shown)
549 && !shell.waitingOnGreeter
550 && !stage.spreadShown
551 }
553 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
554 ? topLevelSurfaceList.focusedWindow.state == Mir.FullscreenState
555 : false
556 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
557 || greeter.hasLockedApp
558 greeterShown: greeter && greeter.shown
559 hasKeyboard: shell.hasKeyboard
560 supportsMultiColorLed: shell.supportsMultiColorLed
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 inverted: shell.usageScenario !== "desktop"
577 superPressed: physicalKeysMapper.superPressed
578 superTabPressed: physicalKeysMapper.superTabPressed
579 panelWidth: units.gu(settings.launcherWidth)
580 lockedVisible: (lockedByUser || shell.atDesktop) && lockAllowed
581 topPanelHeight: panel.panelHeight
582 drawerEnabled: !greeter.active && tutorial.launcherLongSwipeEnabled
583 privateMode: greeter.active
584 background: wallpaperResolver.background
586 // It can be assumed that the Launcher and Panel would overlap if
587 // the Panel is open and taking up the full width of the shell
588 readonly property bool collidingWithPanel: panel && (!panel.fullyClosed && !panel.partialWidth)
590 // The "autohideLauncher" setting is only valid in desktop mode
591 readonly property bool lockedByUser: (shell.usageScenario == "desktop" && !settings.autohideLauncher)
593 // The Launcher should absolutely not be locked visible under some
594 // conditions
595 readonly property bool lockAllowed: !collidingWithPanel && !panel.fullscreenMode && !wizard.active && !tutorial.demonstrateLauncher
597 onShowDashHome: showHome()
598 onLauncherApplicationSelected: {
599 greeter.notifyUserRequestedApp();
600 shell.activateApplication(appId);
601 }
602 onShownChanged: {
603 if (shown) {
604 panel.indicators.hide();
605 panel.applicationMenus.hide();
606 }
607 }
608 onDrawerShownChanged: {
609 if (drawerShown) {
610 panel.indicators.hide();
611 panel.applicationMenus.hide();
612 }
613 }
614 onFocusChanged: {
615 if (!focus) {
616 stage.focus = true;
617 }
618 }
620 GlobalShortcut {
621 shortcut: Qt.MetaModifier | Qt.Key_A
622 onTriggered: {
623 launcher.toggleDrawer(true);
624 }
625 }
626 GlobalShortcut {
627 shortcut: Qt.AltModifier | Qt.Key_F1
628 onTriggered: {
629 launcher.openForKeyboardNavigation();
630 }
631 }
632 GlobalShortcut {
633 shortcut: Qt.MetaModifier | Qt.Key_0
634 onTriggered: {
635 if (LauncherModel.get(9)) {
636 activateApplication(LauncherModel.get(9).appId);
637 }
638 }
639 }
640 Repeater {
641 model: 9
642 GlobalShortcut {
643 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
644 onTriggered: {
645 if (LauncherModel.get(index)) {
646 activateApplication(LauncherModel.get(index).appId);
647 }
648 }
649 }
650 }
651 }
653 KeyboardShortcutsOverlay {
654 objectName: "shortcutsOverlay"
655 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
656 && height < parent.height - padding - panel.panelHeight
657 anchors.centerIn: parent
658 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
659 anchors.verticalCenterOffset: panel.panelHeight/2
660 visible: opacity > 0
661 opacity: enabled ? 0.95 : 0
663 Behavior on opacity {
664 UbuntuNumberAnimation {}
665 }
666 }
668 Tutorial {
669 id: tutorial
670 objectName: "tutorial"
671 anchors.fill: parent
673 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
674 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
675 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
676 inputMethod.visible ||
677 (launcher.shown && !launcher.lockedVisible) ||
678 panel.indicators.shown || stage.rightEdgeDragProgress > 0
679 usageScenario: shell.usageScenario
680 lastInputTimestamp: inputFilter.lastInputTimestamp
681 launcher: launcher
682 panel: panel
683 stage: stage
684 }
686 Wizard {
687 id: wizard
688 objectName: "wizard"
689 anchors.fill: parent
690 deferred: shell.mode === "greeter"
692 function unlockWhenDoneWithWizard() {
693 if (!active) {
694 Connectivity.unlockAllModems();
695 }
696 }
698 Component.onCompleted: unlockWhenDoneWithWizard()
699 onActiveChanged: unlockWhenDoneWithWizard()
700 }
702 MouseArea { // modal notifications prevent interacting with other contents
703 anchors.fill: parent
704 visible: notifications.useModal
705 enabled: visible
706 }
708 Notifications {
709 id: notifications
711 model: NotificationBackend.Model
712 margin: units.gu(1)
713 hasMouse: shell.hasMouse
714 background: wallpaperResolver.background
716 y: topmostIsFullscreen ? 0 : panel.panelHeight
717 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
719 states: [
720 State {
721 name: "narrow"
722 when: overlay.width <= units.gu(60)
723 AnchorChanges {
724 target: notifications
725 anchors.left: parent.left
726 anchors.right: parent.right
727 }
728 },
729 State {
730 name: "wide"
731 when: overlay.width > units.gu(60)
732 AnchorChanges {
733 target: notifications
734 anchors.left: undefined
735 anchors.right: parent.right
736 }
737 PropertyChanges { target: notifications; width: units.gu(38) }
738 }
739 ]
740 }
742 EdgeBarrier {
743 id: rightEdgeBarrier
744 enabled: !greeter.shown
746 // NB: it does its own positioning according to the specified edge
747 edge: Qt.RightEdge
749 onPassed: {
750 panel.indicators.hide()
751 }
753 material: Component {
754 Item {
755 Rectangle {
756 width: parent.height
757 height: parent.width
758 rotation: 90
759 anchors.centerIn: parent
760 gradient: Gradient {
761 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
762 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
763 }
764 }
765 }
766 }
767 }
768 }
770 Dialogs {
771 id: dialogs
772 objectName: "dialogs"
773 anchors.fill: parent
774 visible: hasActiveDialog
775 z: overlay.z + 10
776 usageScenario: shell.usageScenario
777 hasKeyboard: shell.hasKeyboard
778 onPowerOffClicked: {
779 shutdownFadeOutRectangle.enabled = true;
780 shutdownFadeOutRectangle.visible = true;
781 shutdownFadeOut.start();
782 }
783 }
785 Connections {
786 target: SessionBroadcast
787 onShowHome: if (shell.mode !== "greeter") showHome()
788 }
790 URLDispatcher {
791 id: urlDispatcher
792 objectName: "urlDispatcher"
793 active: shell.mode === "greeter"
794 onUrlRequested: shell.activateURL(url)
795 }
797 ItemGrabber {
798 id: itemGrabber
799 anchors.fill: parent
800 z: dialogs.z + 10
801 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
802 Connections {
803 target: stage
804 ignoreUnknownSignals: true
805 onItemSnapshotRequested: itemGrabber.capture(item)
806 }
807 }
809 Timer {
810 id: cursorHidingTimer
811 interval: 3000
812 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
813 onTriggered: cursor.opacity = 0;
814 }
816 Cursor {
817 id: cursor
818 objectName: "cursor"
819 visible: shell.hasMouse
820 z: itemGrabber.z + 1
821 topBoundaryOffset: panel.panelHeight
823 confiningItem: stage.itemConfiningMouseCursor
825 property bool mouseNeverMoved: true
826 Binding {
827 target: cursor; property: "x"; value: shell.width / 2
828 when: cursor.mouseNeverMoved && cursor.visible
829 }
830 Binding {
831 target: cursor; property: "y"; value: shell.height / 2
832 when: cursor.mouseNeverMoved && cursor.visible
833 }
835 height: units.gu(3)
837 readonly property var previewRectangle: stage.previewRectangle.target &&
838 stage.previewRectangle.target.dragging ?
839 stage.previewRectangle : null
841 onPushedLeftBoundary: {
842 if (buttons === Qt.NoButton) {
843 launcher.pushEdge(amount);
844 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
845 previewRectangle.maximizeLeft(amount);
846 }
847 }
849 onPushedRightBoundary: {
850 if (buttons === Qt.NoButton) {
851 rightEdgeBarrier.push(amount);
852 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
853 previewRectangle.maximizeRight(amount);
854 }
855 }
857 onPushedTopBoundary: {
858 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
859 previewRectangle.maximize(amount);
860 }
861 }
862 onPushedTopLeftCorner: {
863 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
864 previewRectangle.maximizeTopLeft(amount);
865 }
866 }
867 onPushedTopRightCorner: {
868 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
869 previewRectangle.maximizeTopRight(amount);
870 }
871 }
872 onPushedBottomLeftCorner: {
873 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
874 previewRectangle.maximizeBottomLeft(amount);
875 }
876 }
877 onPushedBottomRightCorner: {
878 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
879 previewRectangle.maximizeBottomRight(amount);
880 }
881 }
882 onPushStopped: {
883 if (previewRectangle) {
884 previewRectangle.stop();
885 }
886 }
888 onMouseMoved: {
889 mouseNeverMoved = false;
890 cursor.opacity = 1;
891 }
893 Behavior on opacity { UbuntuNumberAnimation {} }
894 }
896 // non-visual objects
897 KeymapSwitcher {
898 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
899 }
900 BrightnessControl {}
902 Rectangle {
903 id: shutdownFadeOutRectangle
904 z: cursor.z + 1
905 enabled: false
906 visible: false
907 color: "black"
908 anchors.fill: parent
909 opacity: 0.0
910 NumberAnimation on opacity {
911 id: shutdownFadeOut
912 from: 0.0
913 to: 1.0
914 onStopped: {
915 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
916 DBusUnitySessionService.shutdown();
917 }
918 }
919 }
920 }