on ded5363 help sign in
unity8/qml/Shell.qml
915 lines 33 kB history raw
1/*
2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
17import QtQuick 2.4
18import QtQuick.Window 2.2
19import AccountsService 0.1
20import Unity.Application 0.1
21import Ubuntu.Components 1.3
22import Ubuntu.Components.Popups 1.3
23import Ubuntu.Gestures 0.1
24import Ubuntu.Telephony 0.1 as Telephony
25import Unity.Connectivity 0.1
26import Unity.Launcher 0.1
27import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
28import GSettings 1.0
29import ImageCache 0.1
30import Utils 0.1
31import Powerd 0.1
32import SessionBroadcast 0.1
33import "Greeter"
34import "Launcher"
35import "Panel"
36import "Components"
37import "Notifications"
38import "Stage"
39import "Tutorial"
40import "Wizard"
41import Unity.Notifications 1.0 as NotificationBackend
42import Unity.Session 0.1
43import Unity.Indicators 0.1 as Indicators
44import Cursor 1.1
45import WindowManager 1.0
48StyledItem {
49 id: shell
51 theme.name: "Ubuntu.Components.Themes.SuruDark"
53 // to be set from outside
54 property int orientationAngle: 0
55 property int orientation
56 property Orientations orientations
57 property real nativeWidth
58 property real nativeHeight
59 property alias panelAreaShowProgress: panel.panelAreaShowProgress
60 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
61 property string mode: "full-greeter"
62 property alias oskEnabled: inputMethod.enabled
63 function updateFocusedAppOrientation() {
64 stage.updateFocusedAppOrientation();
65 }
66 function updateFocusedAppOrientationAnimated() {
67 stage.updateFocusedAppOrientationAnimated();
68 }
69 property bool hasMouse: false
70 property bool hasKeyboard: false
71 property bool hasTouchscreen: false
73 // to be read from outside
74 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
76 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
77 && stage.orientationChangesEnabled
78 && (!greeter || !greeter.animating)
80 readonly property bool showingGreeter: greeter && greeter.shown
82 property bool startingUp: true
83 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
85 property int supportedOrientations: {
86 if (startingUp) {
87 // Ensure we don't rotate during start up
88 return Qt.PrimaryOrientation;
89 } else if (showingGreeter || notifications.topmostIsFullscreen) {
90 return Qt.PrimaryOrientation;
91 } else {
92 return shell.orientations.map(stage.supportedOrientations);
93 }
94 }
96 readonly property var mainApp: stage.mainApp
98 onMainAppChanged: {
99 _onMainAppChanged((mainApp ? mainApp.appId : ""));
100 }
101 Connections {
102 target: ApplicationManager
103 onFocusRequested: {
104 if (shell.mainApp && shell.mainApp.appId === appId) {
105 _onMainAppChanged(appId);
106 }
107 }
108 }
110 // Calls attention back to the most important thing that's been focused
111 // (ex: phone calls go over Wizard, app focuses go over indicators, greeter
112 // goes over everything if it is locked)
113 // Must be called whenever app focus changes occur, even if the focus change
114 // is "nothing is focused". In that case, call with appId = ""
115 function _onMainAppChanged(appId) {
117 if (appId !== "") {
118 if (wizard.active) {
119 // If this happens on first boot, we may be in the
120 // wizard while receiving a call. A call is more
121 // important than the wizard so just bail out of it.
122 wizard.hide();
123 }
125 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
126 // If we are in the middle of a call, make dialer lockedApp. The
127 // Greeter will show it when it's notified of the focus.
128 // This can happen if user backs out of dialer back to greeter, then
129 // launches dialer again.
130 greeter.lockedApp = appId;
131 }
133 panel.indicators.hide();
134 launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
135 } else if (topLevelSurfaceList.count === 0 && greeter && !greeter.active && !wizard.active) {
136 // Show the Launcher when there are no apps running
137 stage.closeSpread();
138 launcher.show();
139 }
141 // *Always* make sure the greeter knows that the focused app changed
142 if (greeter) greeter.notifyAppFocusRequested(appId);
143 }
145 // For autopilot consumption
146 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
148 // Note when greeter is waiting on PAM, so that we can disable edges until
149 // we know which user data to show and whether the session is locked.
150 readonly property bool waitingOnGreeter: greeter && greeter.waiting
152 property real edgeSize: units.gu(settings.edgeDragWidth)
154 WallpaperResolver {
155 id: wallpaperResolver
156 objectName: "wallpaperResolver"
158 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
159 readonly property bool hasCustomBackground: background != defaultBackground
161 // Use a cached version of the scaled-down wallpaper (as sometimes the
162 // image can be quite big compared to the device size, including for
163 // our default wallpaper). We use a name=wallpaper argument here to
164 // make sure we don't litter our cache with lots of scaled images. We
165 // only need to bother caching one at a time.
166 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
168 GSettings {
169 id: backgroundSettings
170 schema.id: "org.gnome.desktop.background"
171 }
173 candidates: [
174 AccountsService.backgroundFile,
175 backgroundSettings.pictureUri,
176 defaultBackground
177 ]
178 }
180 readonly property alias greeter: greeterLoader.item
182 function activateApplication(appId) {
183 topLevelSurfaceList.pendingActivation();
185 // Either open the app in our own session, or -- if we're acting as a
186 // greeter -- ask the user's session to open it for us.
187 if (shell.mode === "greeter") {
188 activateURL("application:///" + appId + ".desktop");
189 } else {
190 startApp(appId);
191 }
192 stage.focus = true;
193 }
195 function activateURL(url) {
196 SessionBroadcast.requestUrlStart(AccountsService.user, url);
197 greeter.notifyUserRequestedApp();
198 panel.indicators.hide();
199 }
201 function startApp(appId) {
202 if (ApplicationManager.findApplication(appId)) {
203 ApplicationManager.requestFocusApplication(appId);
204 } else {
205 ApplicationManager.startApplication(appId);
206 }
207 }
209 function startLockedApp(app) {
210 if (greeter.locked) {
211 greeter.lockedApp = app;
212 }
213 startApp(app); // locked apps are always in our same session
214 }
216 Binding {
217 target: LauncherModel
218 property: "applicationManager"
219 value: ApplicationManager
220 }
222 Component.onCompleted: {
223 finishStartUpTimer.start();
224 }
226 VolumeControl {
227 id: volumeControl
228 }
230 PhysicalKeysMapper {
231 id: physicalKeysMapper
232 objectName: "physicalKeysMapper"
234 onPowerKeyLongPressed: dialogs.showPowerDialog();
235 onVolumeDownTriggered: volumeControl.volumeDown();
236 onVolumeUpTriggered: volumeControl.volumeUp();
237 onScreenshotTriggered: itemGrabber.capture(shell);
238 }
240 GlobalShortcut {
241 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
242 }
244 WindowInputFilter {
245 id: inputFilter
246 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
247 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
248 }
250 WindowInputMonitor {
251 objectName: "windowInputMonitor"
252 onHomeKeyActivated: {
253 // Ignore when greeter is active, to avoid pocket presses
254 if (!greeter.active) {
255 launcher.toggleDrawer(false);
256 }
257 }
258 onTouchBegun: { cursor.opacity = 0; }
259 onTouchEnded: {
260 // move the (hidden) cursor to the last known touch position
261 var mappedCoords = mapFromItem(null, pos.x, pos.y);
262 cursor.x = mappedCoords.x;
263 cursor.y = mappedCoords.y;
264 cursor.mouseNeverMoved = false;
265 }
266 }
268 AvailableDesktopArea {
269 id: availableDesktopAreaItem
270 anchors.fill: parent
271 anchors.topMargin: panel.fullscreenMode ? 0 : panel.minimizedPanelHeight
272 anchors.leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
273 }
275 GSettings {
276 id: settings
277 schema.id: "com.canonical.Unity8"
278 }
280 Item {
281 id: stages
282 objectName: "stages"
283 width: parent.width
284 height: parent.height
286 SurfaceManager {
287 id: surfaceMan
288 objectName: "surfaceManager"
289 }
290 TopLevelWindowModel {
291 id: topLevelSurfaceList
292 objectName: "topLevelSurfaceList"
293 applicationManager: ApplicationManager // it's a singleton
294 surfaceManager: surfaceMan
295 }
297 Stage {
298 id: stage
299 objectName: "stage"
300 anchors.fill: parent
301 focus: true
303 dragAreaWidth: shell.edgeSize
304 background: wallpaperResolver.background
306 applicationManager: ApplicationManager
307 topLevelSurfaceList: topLevelSurfaceList
308 inputMethodRect: inputMethod.visibleRect
309 rightEdgePushProgress: rightEdgeBarrier.progress
310 availableDesktopArea: availableDesktopAreaItem
312 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
313 ? "phone"
314 : shell.usageScenario
316 mode: usageScenario == "phone" ? "staged"
317 : usageScenario == "tablet" ? "stagedWithSideStage"
318 : "windowed"
320 shellOrientation: shell.orientation
321 shellOrientationAngle: shell.orientationAngle
322 orientations: shell.orientations
323 nativeWidth: shell.nativeWidth
324 nativeHeight: shell.nativeHeight
326 allowInteractivity: (!greeter || !greeter.shown)
327 && panel.indicators.fullyClosed
328 && !notifications.useModal
329 && !launcher.takesFocus
331 suspended: greeter.shown
332 altTabPressed: physicalKeysMapper.altTabPressed
333 oskEnabled: shell.oskEnabled
334 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
336 onSpreadShownChanged: {
337 panel.indicators.hide();
338 panel.applicationMenus.hide();
339 }
340 }
342 TouchGestureArea {
343 anchors.fill: stage
345 minimumTouchPoints: 4
346 maximumTouchPoints: minimumTouchPoints
348 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
349 touchPoints.length >= minimumTouchPoints &&
350 touchPoints.length <= maximumTouchPoints
351 property bool wasPressed: false
353 onRecognisedPressChanged: {
354 if (recognisedPress) {
355 wasPressed = true;
356 }
357 }
359 onStatusChanged: {
360 if (status !== TouchGestureArea.Recognized) {
361 if (status === TouchGestureArea.WaitingForTouch) {
362 if (wasPressed && !dragging) {
363 launcher.toggleDrawer(true);
364 }
365 }
366 wasPressed = false;
367 }
368 }
369 }
370 }
372 InputMethod {
373 id: inputMethod
374 objectName: "inputMethod"
375 surface: topLevelSurfaceList.inputMethodSurface
376 anchors {
377 fill: parent
378 topMargin: panel.panelHeight
379 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
380 }
381 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
382 }
384 Loader {
385 id: greeterLoader
386 objectName: "greeterLoader"
387 anchors.fill: parent
388 anchors.topMargin: panel.panelHeight
389 sourceComponent: shell.mode != "shell" ? integratedGreeter :
390 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
391 onLoaded: {
392 item.objectName = "greeter"
393 }
394 property bool toggleDrawerAfterUnlock: false
395 Connections {
396 target: greeter
397 onActiveChanged: {
398 if (greeter.active)
399 return
401 // Show drawer in case showHome() requests it
402 if (greeterLoader.toggleDrawerAfterUnlock) {
403 launcher.toggleDrawer(false);
404 greeterLoader.toggleDrawerAfterUnlock = false;
405 }
406 // Show the launcher when there are no running apps and the user
407 // isn't operating the panel
408 else if (topLevelSurfaceList.count < 1 && !panel.visible) {
409 launcher.switchToNextState("visible")
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.cachedBackground
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 onSessionStarted: {
436 launcher.hide();
437 }
439 onTease: {
440 if (!tutorial.running) {
441 launcher.tease();
442 }
443 }
445 onEmergencyCall: startLockedApp("dialer-app")
446 }
447 }
449 Timer {
450 // See powerConnection for why this is useful
451 id: showGreeterDelayed
452 interval: 1
453 onTriggered: {
454 // Go through the dbus service, because it has checks for whether
455 // we are even allowed to lock or not.
456 DBusUnitySessionService.PromptLock();
457 }
458 }
460 Connections {
461 id: callConnection
462 target: callManager
464 onHasCallsChanged: {
465 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
466 // We just received an incoming call while locked. The
467 // indicator will have already launched dialer-app for us, but
468 // there is a race between "hasCalls" changing and the dialer
469 // starting up. So in case we lose that race, we'll start/
470 // focus the dialer ourselves here too. Even if the indicator
471 // didn't launch the dialer for some reason (or maybe a call
472 // started via some other means), if an active call is
473 // happening, we want to be in the dialer.
474 startLockedApp("dialer-app")
475 }
476 }
477 }
479 Connections {
480 id: powerConnection
481 target: Powerd
483 onStatusChanged: {
484 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
485 !callManager.hasCalls && !wizard.active) {
486 // We don't want to simply call greeter.showNow() here, because
487 // that will take too long. Qt will delay button event
488 // handling until the greeter is done loading and may think the
489 // user held down the power button the whole time, leading to a
490 // power dialog being shown. Instead, delay showing the
491 // greeter until we've finished handling the event. We could
492 // make the greeter load asynchronously instead, but that
493 // introduces a whole host of timing issues, especially with
494 // its animations. So this is simpler.
495 showGreeterDelayed.start();
496 }
497 }
498 }
500 function showHome() {
501 greeter.notifyUserRequestedApp();
503 if (shell.mode === "greeter") {
504 SessionBroadcast.requestHomeShown(AccountsService.user);
505 } else {
506 if (!greeter.active) {
507 launcher.toggleDrawer(false);
508 } else {
509 greeterLoader.toggleDrawerAfterUnlock = true;
510 }
511 }
512 }
514 Item {
515 id: overlay
516 z: 10
518 anchors.fill: parent
520 Panel {
521 id: panel
522 objectName: "panel"
523 anchors.fill: parent //because this draws indicator menus
525 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
526 minimizedPanelHeight: units.gu(3)
527 expandedPanelHeight: units.gu(7)
528 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
529 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
530 applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
532 indicators {
533 hides: [launcher]
534 available: tutorial.panelEnabled
535 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
536 && (!greeter || !greeter.hasLockedApp)
537 && !shell.waitingOnGreeter
538 && settings.enableIndicatorMenu
540 model: Indicators.IndicatorsModel {
541 // tablet and phone both use the same profile
542 // FIXME: use just "phone" for greeter too, but first fix
543 // greeter app launching to either load the app inside the
544 // greeter or tell the session to load the app. This will
545 // involve taking the url-dispatcher dbus name and using
546 // SessionBroadcast to tell the session.
547 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
548 Component.onCompleted: load();
549 }
550 }
552 applicationMenus {
553 hides: [launcher]
554 available: (!greeter || !greeter.shown)
555 && !shell.waitingOnGreeter
556 && !stage.spreadShown
557 }
559 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
560 ? topLevelSurfaceList.focusedWindow.state == Mir.FullscreenState
561 : false
562 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
563 || greeter.hasLockedApp
564 greeterShown: greeter && greeter.shown
565 hasKeyboard: shell.hasKeyboard
566 }
568 Launcher {
569 id: launcher
570 objectName: "launcher"
572 anchors.top: parent.top
573 anchors.topMargin: inverted ? 0 : panel.panelHeight
574 anchors.bottom: parent.bottom
575 width: parent.width
576 dragAreaWidth: shell.edgeSize
577 available: tutorial.launcherEnabled
578 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
579 && !greeter.hasLockedApp
580 && !shell.waitingOnGreeter
581 inverted: shell.usageScenario !== "desktop"
582 superPressed: physicalKeysMapper.superPressed
583 superTabPressed: physicalKeysMapper.superTabPressed
584 panelWidth: units.gu(settings.launcherWidth)
585 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
586 topPanelHeight: panel.panelHeight
587 drawerEnabled: !greeter.active
588 privateMode: greeter.active
589 background: wallpaperResolver.cachedBackground
591 onShowDashHome: showHome()
592 onLauncherApplicationSelected: {
593 greeter.notifyUserRequestedApp();
594 shell.activateApplication(appId);
595 }
596 onShownChanged: {
597 if (shown) {
598 panel.indicators.hide();
599 panel.applicationMenus.hide();
600 }
601 }
602 onDrawerShownChanged: {
603 if (drawerShown) {
604 panel.indicators.hide();
605 panel.applicationMenus.hide();
606 }
607 }
608 onFocusChanged: {
609 if (!focus) {
610 stage.focus = true;
611 }
612 }
614 GlobalShortcut {
615 shortcut: Qt.MetaModifier | Qt.Key_A
616 onTriggered: {
617 launcher.toggleDrawer(true);
618 }
619 }
620 GlobalShortcut {
621 shortcut: Qt.AltModifier | Qt.Key_F1
622 onTriggered: {
623 launcher.openForKeyboardNavigation();
624 }
625 }
626 GlobalShortcut {
627 shortcut: Qt.MetaModifier | Qt.Key_0
628 onTriggered: {
629 if (LauncherModel.get(9)) {
630 activateApplication(LauncherModel.get(9).appId);
631 }
632 }
633 }
634 Repeater {
635 model: 9
636 GlobalShortcut {
637 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
638 onTriggered: {
639 if (LauncherModel.get(index)) {
640 activateApplication(LauncherModel.get(index).appId);
641 }
642 }
643 }
644 }
645 }
647 KeyboardShortcutsOverlay {
648 objectName: "shortcutsOverlay"
649 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
650 && height < parent.height - padding - panel.panelHeight
651 anchors.centerIn: parent
652 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
653 anchors.verticalCenterOffset: panel.panelHeight/2
654 visible: opacity > 0
655 opacity: enabled ? 0.95 : 0
657 Behavior on opacity {
658 UbuntuNumberAnimation {}
659 }
660 }
662 Tutorial {
663 id: tutorial
664 objectName: "tutorial"
665 anchors.fill: parent
667 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
668 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
669 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
670 inputMethod.visible ||
671 (launcher.shown && !launcher.lockedVisible) ||
672 panel.indicators.shown || stage.rightEdgeDragProgress > 0
673 usageScenario: shell.usageScenario
674 lastInputTimestamp: inputFilter.lastInputTimestamp
675 launcher: launcher
676 panel: panel
677 stage: stage
678 }
680 Wizard {
681 id: wizard
682 objectName: "wizard"
683 anchors.fill: parent
684 deferred: shell.mode === "greeter"
686 function unlockWhenDoneWithWizard() {
687 if (!active) {
688 Connectivity.unlockAllModems();
689 }
690 }
692 Component.onCompleted: unlockWhenDoneWithWizard()
693 onActiveChanged: unlockWhenDoneWithWizard()
694 }
696 MouseArea { // modal notifications prevent interacting with other contents
697 anchors.fill: parent
698 visible: notifications.useModal
699 enabled: visible
700 }
702 Notifications {
703 id: notifications
705 model: NotificationBackend.Model
706 margin: units.gu(1)
707 hasMouse: shell.hasMouse
708 background: wallpaperResolver.cachedBackground
710 y: topmostIsFullscreen ? 0 : panel.panelHeight
711 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
713 states: [
714 State {
715 name: "narrow"
716 when: overlay.width <= units.gu(60)
717 AnchorChanges {
718 target: notifications
719 anchors.left: parent.left
720 anchors.right: parent.right
721 }
722 },
723 State {
724 name: "wide"
725 when: overlay.width > units.gu(60)
726 AnchorChanges {
727 target: notifications
728 anchors.left: undefined
729 anchors.right: parent.right
730 }
731 PropertyChanges { target: notifications; width: units.gu(38) }
732 }
733 ]
734 }
736 EdgeBarrier {
737 id: rightEdgeBarrier
738 enabled: !greeter.shown
740 // NB: it does its own positioning according to the specified edge
741 edge: Qt.RightEdge
743 onPassed: {
744 panel.indicators.hide()
745 }
747 material: Component {
748 Item {
749 Rectangle {
750 width: parent.height
751 height: parent.width
752 rotation: 90
753 anchors.centerIn: parent
754 gradient: Gradient {
755 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
756 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
757 }
758 }
759 }
760 }
761 }
762 }
764 Dialogs {
765 id: dialogs
766 objectName: "dialogs"
767 anchors.fill: parent
768 visible: hasActiveDialog
769 z: overlay.z + 10
770 usageScenario: shell.usageScenario
771 hasKeyboard: shell.hasKeyboard
772 onPowerOffClicked: {
773 shutdownFadeOutRectangle.enabled = true;
774 shutdownFadeOutRectangle.visible = true;
775 shutdownFadeOut.start();
776 }
777 }
779 Connections {
780 target: SessionBroadcast
781 onShowHome: if (shell.mode !== "greeter") showHome()
782 }
784 URLDispatcher {
785 id: urlDispatcher
786 objectName: "urlDispatcher"
787 active: shell.mode === "greeter"
788 onUrlRequested: shell.activateURL(url)
789 }
791 ItemGrabber {
792 id: itemGrabber
793 anchors.fill: parent
794 z: dialogs.z + 10
795 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
796 Connections {
797 target: stage
798 ignoreUnknownSignals: true
799 onItemSnapshotRequested: itemGrabber.capture(item)
800 }
801 }
803 Timer {
804 id: cursorHidingTimer
805 interval: 3000
806 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
807 onTriggered: cursor.opacity = 0;
808 }
810 Cursor {
811 id: cursor
812 objectName: "cursor"
813 visible: shell.hasMouse
814 z: itemGrabber.z + 1
815 topBoundaryOffset: panel.panelHeight
817 confiningItem: stage.itemConfiningMouseCursor
819 property bool mouseNeverMoved: true
820 Binding {
821 target: cursor; property: "x"; value: shell.width / 2
822 when: cursor.mouseNeverMoved && cursor.visible
823 }
824 Binding {
825 target: cursor; property: "y"; value: shell.height / 2
826 when: cursor.mouseNeverMoved && cursor.visible
827 }
829 height: units.gu(3)
831 readonly property var previewRectangle: stage.previewRectangle.target &&
832 stage.previewRectangle.target.dragging ?
833 stage.previewRectangle : null
835 onPushedLeftBoundary: {
836 if (buttons === Qt.NoButton) {
837 launcher.pushEdge(amount);
838 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
839 previewRectangle.maximizeLeft(amount);
840 }
841 }
843 onPushedRightBoundary: {
844 if (buttons === Qt.NoButton) {
845 rightEdgeBarrier.push(amount);
846 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
847 previewRectangle.maximizeRight(amount);
848 }
849 }
851 onPushedTopBoundary: {
852 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
853 previewRectangle.maximize(amount);
854 }
855 }
856 onPushedTopLeftCorner: {
857 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
858 previewRectangle.maximizeTopLeft(amount);
859 }
860 }
861 onPushedTopRightCorner: {
862 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
863 previewRectangle.maximizeTopRight(amount);
864 }
865 }
866 onPushedBottomLeftCorner: {
867 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
868 previewRectangle.maximizeBottomLeft(amount);
869 }
870 }
871 onPushedBottomRightCorner: {
872 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
873 previewRectangle.maximizeBottomRight(amount);
874 }
875 }
876 onPushStopped: {
877 if (previewRectangle) {
878 previewRectangle.stop();
879 }
880 }
882 onMouseMoved: {
883 mouseNeverMoved = false;
884 cursor.opacity = 1;
885 }
887 Behavior on opacity { UbuntuNumberAnimation {} }
888 }
890 // non-visual objects
891 KeymapSwitcher {
892 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
893 }
894 BrightnessControl {}
896 Rectangle {
897 id: shutdownFadeOutRectangle
898 z: cursor.z + 1
899 enabled: false
900 visible: false
901 color: "black"
902 anchors.fill: parent
903 opacity: 0.0
904 NumberAnimation on opacity {
905 id: shutdownFadeOut
906 from: 0.0
907 to: 1.0
908 onStopped: {
909 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
910 DBusUnitySessionService.shutdown();
911 }
912 }
913 }
914 }