on b76b31e help sign in
unity8/qml/Shell.qml
913 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 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 // Either open the app in our own session, or -- if we're acting as a
184 // greeter -- ask the user's session to open it for us.
185 if (shell.mode === "greeter") {
186 activateURL("application:///" + appId + ".desktop");
187 } else {
188 startApp(appId);
189 }
190 stage.focus = true;
191 }
193 function activateURL(url) {
194 SessionBroadcast.requestUrlStart(AccountsService.user, url);
195 greeter.notifyUserRequestedApp();
196 panel.indicators.hide();
197 }
199 function startApp(appId) {
200 if (ApplicationManager.findApplication(appId)) {
201 ApplicationManager.requestFocusApplication(appId);
202 } else {
203 ApplicationManager.startApplication(appId);
204 }
205 }
207 function startLockedApp(app) {
208 if (greeter.locked) {
209 greeter.lockedApp = app;
210 }
211 startApp(app); // locked apps are always in our same session
212 }
214 Binding {
215 target: LauncherModel
216 property: "applicationManager"
217 value: ApplicationManager
218 }
220 Component.onCompleted: {
221 finishStartUpTimer.start();
222 }
224 VolumeControl {
225 id: volumeControl
226 }
228 PhysicalKeysMapper {
229 id: physicalKeysMapper
230 objectName: "physicalKeysMapper"
232 onPowerKeyLongPressed: dialogs.showPowerDialog();
233 onVolumeDownTriggered: volumeControl.volumeDown();
234 onVolumeUpTriggered: volumeControl.volumeUp();
235 onScreenshotTriggered: itemGrabber.capture(shell);
236 }
238 GlobalShortcut {
239 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
240 }
242 WindowInputFilter {
243 id: inputFilter
244 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
245 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
246 }
248 WindowInputMonitor {
249 objectName: "windowInputMonitor"
250 onHomeKeyActivated: {
251 // Ignore when greeter is active, to avoid pocket presses
252 if (!greeter.active) {
253 launcher.toggleDrawer(false);
254 }
255 }
256 onTouchBegun: { cursor.opacity = 0; }
257 onTouchEnded: {
258 // move the (hidden) cursor to the last known touch position
259 var mappedCoords = mapFromItem(null, pos.x, pos.y);
260 cursor.x = mappedCoords.x;
261 cursor.y = mappedCoords.y;
262 cursor.mouseNeverMoved = false;
263 }
264 }
266 AvailableDesktopArea {
267 id: availableDesktopAreaItem
268 anchors.fill: parent
269 anchors.topMargin: panel.fullscreenMode ? 0 : panel.minimizedPanelHeight
270 anchors.leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
271 }
273 GSettings {
274 id: settings
275 schema.id: "com.canonical.Unity8"
276 }
278 Item {
279 id: stages
280 objectName: "stages"
281 width: parent.width
282 height: parent.height
284 SurfaceManager {
285 id: surfaceMan
286 objectName: "surfaceManager"
287 }
288 TopLevelWindowModel {
289 id: topLevelSurfaceList
290 objectName: "topLevelSurfaceList"
291 applicationManager: ApplicationManager // it's a singleton
292 surfaceManager: surfaceMan
293 }
295 Stage {
296 id: stage
297 objectName: "stage"
298 anchors.fill: parent
299 focus: true
301 dragAreaWidth: shell.edgeSize
302 background: wallpaperResolver.background
304 applicationManager: ApplicationManager
305 topLevelSurfaceList: topLevelSurfaceList
306 inputMethodRect: inputMethod.visibleRect
307 rightEdgePushProgress: rightEdgeBarrier.progress
308 availableDesktopArea: availableDesktopAreaItem
310 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
311 ? "phone"
312 : shell.usageScenario
314 mode: usageScenario == "phone" ? "staged"
315 : usageScenario == "tablet" ? "stagedWithSideStage"
316 : "windowed"
318 shellOrientation: shell.orientation
319 shellOrientationAngle: shell.orientationAngle
320 orientations: shell.orientations
321 nativeWidth: shell.nativeWidth
322 nativeHeight: shell.nativeHeight
324 interactive: (!greeter || !greeter.shown)
325 && panel.indicators.fullyClosed
326 && !notifications.useModal
327 && !launcher.takesFocus
329 suspended: greeter.shown
330 altTabPressed: physicalKeysMapper.altTabPressed
331 oskEnabled: shell.oskEnabled
332 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
334 onSpreadShownChanged: {
335 panel.indicators.hide();
336 panel.applicationMenus.hide();
337 }
338 }
340 TouchGestureArea {
341 anchors.fill: stage
343 minimumTouchPoints: 4
344 maximumTouchPoints: minimumTouchPoints
346 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
347 touchPoints.length >= minimumTouchPoints &&
348 touchPoints.length <= maximumTouchPoints
349 property bool wasPressed: false
351 onRecognisedPressChanged: {
352 if (recognisedPress) {
353 wasPressed = true;
354 }
355 }
357 onStatusChanged: {
358 if (status !== TouchGestureArea.Recognized) {
359 if (status === TouchGestureArea.WaitingForTouch) {
360 if (wasPressed && !dragging) {
361 launcher.toggleDrawer(true);
362 }
363 }
364 wasPressed = false;
365 }
366 }
367 }
368 }
370 InputMethod {
371 id: inputMethod
372 objectName: "inputMethod"
373 surface: topLevelSurfaceList.inputMethodSurface
374 anchors {
375 fill: parent
376 topMargin: panel.panelHeight
377 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
378 }
379 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
380 }
382 Loader {
383 id: greeterLoader
384 objectName: "greeterLoader"
385 anchors.fill: parent
386 anchors.topMargin: panel.panelHeight
387 sourceComponent: shell.mode != "shell" ? integratedGreeter :
388 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
389 onLoaded: {
390 item.objectName = "greeter"
391 }
392 property bool toggleDrawerAfterUnlock: false
393 Connections {
394 target: greeter
395 onActiveChanged: {
396 if (greeter.active)
397 return
399 // Show drawer in case showHome() requests it
400 if (greeterLoader.toggleDrawerAfterUnlock) {
401 launcher.toggleDrawer(false);
402 greeterLoader.toggleDrawerAfterUnlock = false;
403 }
404 // Show the launcher when there are no running apps
405 else if (topLevelSurfaceList.count < 1) {
406 launcher.switchToNextState("visible")
407 }
408 }
409 }
410 }
412 Component {
413 id: integratedGreeter
414 Greeter {
416 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
417 hides: [launcher, panel.indicators, panel.applicationMenus]
418 tabletMode: shell.usageScenario != "phone"
419 forcedUnlock: wizard.active || shell.mode === "full-shell"
420 background: wallpaperResolver.cachedBackground
421 hasCustomBackground: wallpaperResolver.hasCustomBackground
422 allowFingerprint: !dialogs.hasActiveDialog &&
423 !notifications.topmostIsFullscreen &&
424 !panel.indicators.shown
426 // avoid overlapping with Launcher's edge drag area
427 // FIXME: Fix TouchRegistry & friends and remove this workaround
428 // Issue involves launcher's DDA getting disabled on a long
429 // left-edge drag
430 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
432 onSessionStarted: {
433 launcher.hide();
434 }
436 onTease: {
437 if (!tutorial.running) {
438 launcher.tease();
439 }
440 }
442 onEmergencyCall: startLockedApp("dialer-app")
443 }
444 }
446 Timer {
447 // See powerConnection for why this is useful
448 id: showGreeterDelayed
449 interval: 1
450 onTriggered: {
451 // Go through the dbus service, because it has checks for whether
452 // we are even allowed to lock or not.
453 DBusUnitySessionService.PromptLock();
454 }
455 }
457 Connections {
458 id: callConnection
459 target: callManager
461 onHasCallsChanged: {
462 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
463 // We just received an incoming call while locked. The
464 // indicator will have already launched dialer-app for us, but
465 // there is a race between "hasCalls" changing and the dialer
466 // starting up. So in case we lose that race, we'll start/
467 // focus the dialer ourselves here too. Even if the indicator
468 // didn't launch the dialer for some reason (or maybe a call
469 // started via some other means), if an active call is
470 // happening, we want to be in the dialer.
471 startLockedApp("dialer-app")
472 }
473 }
474 }
476 Connections {
477 id: powerConnection
478 target: Powerd
480 onStatusChanged: {
481 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
482 !callManager.hasCalls && !wizard.active) {
483 // We don't want to simply call greeter.showNow() here, because
484 // that will take too long. Qt will delay button event
485 // handling until the greeter is done loading and may think the
486 // user held down the power button the whole time, leading to a
487 // power dialog being shown. Instead, delay showing the
488 // greeter until we've finished handling the event. We could
489 // make the greeter load asynchronously instead, but that
490 // introduces a whole host of timing issues, especially with
491 // its animations. So this is simpler.
492 showGreeterDelayed.start();
493 }
494 }
495 }
497 function showHome() {
498 greeter.notifyUserRequestedApp();
500 if (shell.mode === "greeter") {
501 SessionBroadcast.requestHomeShown(AccountsService.user);
502 } else {
503 if (!greeter.active) {
504 launcher.toggleDrawer(false);
505 } else {
506 greeterLoader.toggleDrawerAfterUnlock = true;
507 }
508 }
509 }
511 Item {
512 id: overlay
513 z: 10
515 anchors.fill: parent
517 Panel {
518 id: panel
519 objectName: "panel"
520 anchors.fill: parent //because this draws indicator menus
522 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
523 minimizedPanelHeight: units.gu(3)
524 expandedPanelHeight: units.gu(7)
525 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
526 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
527 applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
529 indicators {
530 hides: [launcher]
531 available: tutorial.panelEnabled
532 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
533 && (!greeter || !greeter.hasLockedApp)
534 && !shell.waitingOnGreeter
535 && settings.enableIndicatorMenu
537 model: Indicators.IndicatorsModel {
538 // tablet and phone both use the same profile
539 // FIXME: use just "phone" for greeter too, but first fix
540 // greeter app launching to either load the app inside the
541 // greeter or tell the session to load the app. This will
542 // involve taking the url-dispatcher dbus name and using
543 // SessionBroadcast to tell the session.
544 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
545 Component.onCompleted: load();
546 }
547 }
549 applicationMenus {
550 hides: [launcher]
551 available: (!greeter || !greeter.shown)
552 && !shell.waitingOnGreeter
553 && !stage.spreadShown
554 }
556 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
557 ? topLevelSurfaceList.focusedWindow.state == Mir.FullscreenState
558 : false
559 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
560 || greeter.hasLockedApp
561 greeterShown: greeter && greeter.shown
562 hasKeyboard: shell.hasKeyboard
563 }
565 Launcher {
566 id: launcher
567 objectName: "launcher"
569 anchors.top: parent.top
570 anchors.topMargin: inverted ? 0 : panel.panelHeight
571 anchors.bottom: parent.bottom
572 width: parent.width
573 dragAreaWidth: shell.edgeSize
574 available: tutorial.launcherEnabled
575 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
576 && !greeter.hasLockedApp
577 && !shell.waitingOnGreeter
578 && settings.enableLauncher
579 inverted: shell.usageScenario !== "desktop"
580 superPressed: physicalKeysMapper.superPressed
581 superTabPressed: physicalKeysMapper.superTabPressed
582 panelWidth: units.gu(settings.launcherWidth)
583 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
584 topPanelHeight: panel.panelHeight
585 drawerEnabled: !greeter.active
586 privateMode: greeter.active
587 background: wallpaperResolver.cachedBackground
589 onShowDashHome: showHome()
590 onLauncherApplicationSelected: {
591 greeter.notifyUserRequestedApp();
592 shell.activateApplication(appId);
593 }
594 onShownChanged: {
595 if (shown) {
596 panel.indicators.hide();
597 panel.applicationMenus.hide();
598 }
599 }
600 onDrawerShownChanged: {
601 if (drawerShown) {
602 panel.indicators.hide();
603 panel.applicationMenus.hide();
604 }
605 }
606 onFocusChanged: {
607 if (!focus) {
608 stage.focus = true;
609 }
610 }
612 GlobalShortcut {
613 shortcut: Qt.MetaModifier | Qt.Key_A
614 onTriggered: {
615 launcher.toggleDrawer(true);
616 }
617 }
618 GlobalShortcut {
619 shortcut: Qt.AltModifier | Qt.Key_F1
620 onTriggered: {
621 launcher.openForKeyboardNavigation();
622 }
623 }
624 GlobalShortcut {
625 shortcut: Qt.MetaModifier | Qt.Key_0
626 onTriggered: {
627 if (LauncherModel.get(9)) {
628 activateApplication(LauncherModel.get(9).appId);
629 }
630 }
631 }
632 Repeater {
633 model: 9
634 GlobalShortcut {
635 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
636 onTriggered: {
637 if (LauncherModel.get(index)) {
638 activateApplication(LauncherModel.get(index).appId);
639 }
640 }
641 }
642 }
643 }
645 KeyboardShortcutsOverlay {
646 objectName: "shortcutsOverlay"
647 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
648 && height < parent.height - padding - panel.panelHeight
649 anchors.centerIn: parent
650 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
651 anchors.verticalCenterOffset: panel.panelHeight/2
652 visible: opacity > 0
653 opacity: enabled ? 0.95 : 0
655 Behavior on opacity {
656 UbuntuNumberAnimation {}
657 }
658 }
660 Tutorial {
661 id: tutorial
662 objectName: "tutorial"
663 anchors.fill: parent
665 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
666 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
667 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
668 inputMethod.visible ||
669 (launcher.shown && !launcher.lockedVisible) ||
670 panel.indicators.shown || stage.rightEdgeDragProgress > 0
671 usageScenario: shell.usageScenario
672 lastInputTimestamp: inputFilter.lastInputTimestamp
673 launcher: launcher
674 panel: panel
675 stage: stage
676 }
678 Wizard {
679 id: wizard
680 objectName: "wizard"
681 anchors.fill: parent
682 deferred: shell.mode === "greeter"
684 function unlockWhenDoneWithWizard() {
685 if (!active) {
686 Connectivity.unlockAllModems();
687 }
688 }
690 Component.onCompleted: unlockWhenDoneWithWizard()
691 onActiveChanged: unlockWhenDoneWithWizard()
692 }
694 MouseArea { // modal notifications prevent interacting with other contents
695 anchors.fill: parent
696 visible: notifications.useModal
697 enabled: visible
698 }
700 Notifications {
701 id: notifications
703 model: NotificationBackend.Model
704 margin: units.gu(1)
705 hasMouse: shell.hasMouse
706 background: wallpaperResolver.cachedBackground
708 y: topmostIsFullscreen ? 0 : panel.panelHeight
709 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
711 states: [
712 State {
713 name: "narrow"
714 when: overlay.width <= units.gu(60)
715 AnchorChanges {
716 target: notifications
717 anchors.left: parent.left
718 anchors.right: parent.right
719 }
720 },
721 State {
722 name: "wide"
723 when: overlay.width > units.gu(60)
724 AnchorChanges {
725 target: notifications
726 anchors.left: undefined
727 anchors.right: parent.right
728 }
729 PropertyChanges { target: notifications; width: units.gu(38) }
730 }
731 ]
732 }
734 EdgeBarrier {
735 id: rightEdgeBarrier
736 enabled: !greeter.shown
738 // NB: it does its own positioning according to the specified edge
739 edge: Qt.RightEdge
741 onPassed: {
742 panel.indicators.hide()
743 }
745 material: Component {
746 Item {
747 Rectangle {
748 width: parent.height
749 height: parent.width
750 rotation: 90
751 anchors.centerIn: parent
752 gradient: Gradient {
753 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
754 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
755 }
756 }
757 }
758 }
759 }
760 }
762 Dialogs {
763 id: dialogs
764 objectName: "dialogs"
765 anchors.fill: parent
766 visible: hasActiveDialog
767 z: overlay.z + 10
768 usageScenario: shell.usageScenario
769 hasKeyboard: shell.hasKeyboard
770 onPowerOffClicked: {
771 shutdownFadeOutRectangle.enabled = true;
772 shutdownFadeOutRectangle.visible = true;
773 shutdownFadeOut.start();
774 }
775 }
777 Connections {
778 target: SessionBroadcast
779 onShowHome: if (shell.mode !== "greeter") showHome()
780 }
782 URLDispatcher {
783 id: urlDispatcher
784 objectName: "urlDispatcher"
785 active: shell.mode === "greeter"
786 onUrlRequested: shell.activateURL(url)
787 }
789 ItemGrabber {
790 id: itemGrabber
791 anchors.fill: parent
792 z: dialogs.z + 10
793 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
794 Connections {
795 target: stage
796 ignoreUnknownSignals: true
797 onItemSnapshotRequested: itemGrabber.capture(item)
798 }
799 }
801 Timer {
802 id: cursorHidingTimer
803 interval: 3000
804 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
805 onTriggered: cursor.opacity = 0;
806 }
808 Cursor {
809 id: cursor
810 objectName: "cursor"
811 visible: shell.hasMouse
812 z: itemGrabber.z + 1
813 topBoundaryOffset: panel.panelHeight
815 confiningItem: stage.itemConfiningMouseCursor
817 property bool mouseNeverMoved: true
818 Binding {
819 target: cursor; property: "x"; value: shell.width / 2
820 when: cursor.mouseNeverMoved && cursor.visible
821 }
822 Binding {
823 target: cursor; property: "y"; value: shell.height / 2
824 when: cursor.mouseNeverMoved && cursor.visible
825 }
827 height: units.gu(3)
829 readonly property var previewRectangle: stage.previewRectangle.target &&
830 stage.previewRectangle.target.dragging ?
831 stage.previewRectangle : null
833 onPushedLeftBoundary: {
834 if (buttons === Qt.NoButton) {
835 launcher.pushEdge(amount);
836 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
837 previewRectangle.maximizeLeft(amount);
838 }
839 }
841 onPushedRightBoundary: {
842 if (buttons === Qt.NoButton) {
843 rightEdgeBarrier.push(amount);
844 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
845 previewRectangle.maximizeRight(amount);
846 }
847 }
849 onPushedTopBoundary: {
850 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
851 previewRectangle.maximize(amount);
852 }
853 }
854 onPushedTopLeftCorner: {
855 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
856 previewRectangle.maximizeTopLeft(amount);
857 }
858 }
859 onPushedTopRightCorner: {
860 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
861 previewRectangle.maximizeTopRight(amount);
862 }
863 }
864 onPushedBottomLeftCorner: {
865 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
866 previewRectangle.maximizeBottomLeft(amount);
867 }
868 }
869 onPushedBottomRightCorner: {
870 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
871 previewRectangle.maximizeBottomRight(amount);
872 }
873 }
874 onPushStopped: {
875 if (previewRectangle) {
876 previewRectangle.stop();
877 }
878 }
880 onMouseMoved: {
881 mouseNeverMoved = false;
882 cursor.opacity = 1;
883 }
885 Behavior on opacity { UbuntuNumberAnimation {} }
886 }
888 // non-visual objects
889 KeymapSwitcher {
890 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
891 }
892 BrightnessControl {}
894 Rectangle {
895 id: shutdownFadeOutRectangle
896 z: cursor.z + 1
897 enabled: false
898 visible: false
899 color: "black"
900 anchors.fill: parent
901 opacity: 0.0
902 NumberAnimation on opacity {
903 id: shutdownFadeOut
904 from: 0.0
905 to: 1.0
906 onStopped: {
907 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
908 DBusUnitySessionService.shutdown();
909 }
910 }
911 }
912 }