on b377476 help sign in
unity8/qml/Shell.qml
914 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.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 interactive: (!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
407 else if (topLevelSurfaceList.count < 1) {
408 launcher.switchToNextState("visible")
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.cachedBackground
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 onSessionStarted: {
435 launcher.hide();
436 }
438 onTease: {
439 if (!tutorial.running) {
440 launcher.tease();
441 }
442 }
444 onEmergencyCall: startLockedApp("dialer-app")
445 }
446 }
448 Timer {
449 // See powerConnection for why this is useful
450 id: showGreeterDelayed
451 interval: 1
452 onTriggered: {
453 // Go through the dbus service, because it has checks for whether
454 // we are even allowed to lock or not.
455 DBusUnitySessionService.PromptLock();
456 }
457 }
459 Connections {
460 id: callConnection
461 target: callManager
463 onHasCallsChanged: {
464 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
465 // We just received an incoming call while locked. The
466 // indicator will have already launched dialer-app for us, but
467 // there is a race between "hasCalls" changing and the dialer
468 // starting up. So in case we lose that race, we'll start/
469 // focus the dialer ourselves here too. Even if the indicator
470 // didn't launch the dialer for some reason (or maybe a call
471 // started via some other means), if an active call is
472 // happening, we want to be in the dialer.
473 startLockedApp("dialer-app")
474 }
475 }
476 }
478 Connections {
479 id: powerConnection
480 target: Powerd
482 onStatusChanged: {
483 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
484 !callManager.hasCalls && !wizard.active) {
485 // We don't want to simply call greeter.showNow() here, because
486 // that will take too long. Qt will delay button event
487 // handling until the greeter is done loading and may think the
488 // user held down the power button the whole time, leading to a
489 // power dialog being shown. Instead, delay showing the
490 // greeter until we've finished handling the event. We could
491 // make the greeter load asynchronously instead, but that
492 // introduces a whole host of timing issues, especially with
493 // its animations. So this is simpler.
494 showGreeterDelayed.start();
495 }
496 }
497 }
499 function showHome() {
500 greeter.notifyUserRequestedApp();
502 if (shell.mode === "greeter") {
503 SessionBroadcast.requestHomeShown(AccountsService.user);
504 } else {
505 if (!greeter.active) {
506 launcher.toggleDrawer(false);
507 } else {
508 greeterLoader.toggleDrawerAfterUnlock = true;
509 }
510 }
511 }
513 Item {
514 id: overlay
515 z: 10
517 anchors.fill: parent
519 Panel {
520 id: panel
521 objectName: "panel"
522 anchors.fill: parent //because this draws indicator menus
524 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
525 minimizedPanelHeight: units.gu(3)
526 expandedPanelHeight: units.gu(7)
527 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
528 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
529 applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
531 indicators {
532 hides: [launcher]
533 available: tutorial.panelEnabled
534 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
535 && (!greeter || !greeter.hasLockedApp)
536 && !shell.waitingOnGreeter
537 && settings.enableIndicatorMenu
539 model: Indicators.IndicatorsModel {
540 // tablet and phone both use the same profile
541 // FIXME: use just "phone" for greeter too, but first fix
542 // greeter app launching to either load the app inside the
543 // greeter or tell the session to load the app. This will
544 // involve taking the url-dispatcher dbus name and using
545 // SessionBroadcast to tell the session.
546 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
547 Component.onCompleted: load();
548 }
549 }
551 applicationMenus {
552 hides: [launcher]
553 available: (!greeter || !greeter.shown)
554 && !shell.waitingOnGreeter
555 && !stage.spreadShown
556 }
558 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
559 ? topLevelSurfaceList.focusedWindow.state == Mir.FullscreenState
560 : false
561 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
562 || greeter.hasLockedApp
563 greeterShown: greeter && greeter.shown
564 hasKeyboard: shell.hasKeyboard
565 }
567 Launcher {
568 id: launcher
569 objectName: "launcher"
571 anchors.top: parent.top
572 anchors.topMargin: inverted ? 0 : panel.panelHeight
573 anchors.bottom: parent.bottom
574 width: parent.width
575 dragAreaWidth: shell.edgeSize
576 available: tutorial.launcherEnabled
577 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
578 && !greeter.hasLockedApp
579 && !shell.waitingOnGreeter
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 topPanelHeight: panel.panelHeight
586 drawerEnabled: !greeter.active
587 privateMode: greeter.active
588 background: wallpaperResolver.cachedBackground
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.toggleDrawer(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"
812 visible: shell.hasMouse
813 z: itemGrabber.z + 1
814 topBoundaryOffset: panel.panelHeight
816 confiningItem: stage.itemConfiningMouseCursor
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 height: units.gu(3)
830 readonly property var previewRectangle: stage.previewRectangle.target &&
831 stage.previewRectangle.target.dragging ?
832 stage.previewRectangle : null
834 onPushedLeftBoundary: {
835 if (buttons === Qt.NoButton) {
836 launcher.pushEdge(amount);
837 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
838 previewRectangle.maximizeLeft(amount);
839 }
840 }
842 onPushedRightBoundary: {
843 if (buttons === Qt.NoButton) {
844 rightEdgeBarrier.push(amount);
845 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
846 previewRectangle.maximizeRight(amount);
847 }
848 }
850 onPushedTopBoundary: {
851 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
852 previewRectangle.maximize(amount);
853 }
854 }
855 onPushedTopLeftCorner: {
856 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
857 previewRectangle.maximizeTopLeft(amount);
858 }
859 }
860 onPushedTopRightCorner: {
861 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
862 previewRectangle.maximizeTopRight(amount);
863 }
864 }
865 onPushedBottomLeftCorner: {
866 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
867 previewRectangle.maximizeBottomLeft(amount);
868 }
869 }
870 onPushedBottomRightCorner: {
871 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
872 previewRectangle.maximizeBottomRight(amount);
873 }
874 }
875 onPushStopped: {
876 if (previewRectangle) {
877 previewRectangle.stop();
878 }
879 }
881 onMouseMoved: {
882 mouseNeverMoved = false;
883 cursor.opacity = 1;
884 }
886 Behavior on opacity { UbuntuNumberAnimation {} }
887 }
889 // non-visual objects
890 KeymapSwitcher {
891 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
892 }
893 BrightnessControl {}
895 Rectangle {
896 id: shutdownFadeOutRectangle
897 z: cursor.z + 1
898 enabled: false
899 visible: false
900 color: "black"
901 anchors.fill: parent
902 opacity: 0.0
903 NumberAnimation on opacity {
904 id: shutdownFadeOut
905 from: 0.0
906 to: 1.0
907 onStopped: {
908 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
909 DBusUnitySessionService.shutdown();
910 }
911 }
912 }
913 }