on 46da459 help sign in
unity8/qml/Shell.qml
918 lines 32 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 "Components/PanelState"
42import Unity.Notifications 1.0 as NotificationBackend
43import Unity.Session 0.1
44import Unity.DashCommunicator 0.1
45import Unity.Indicators 0.1 as Indicators
46import Cursor 1.1
47import WindowManager 1.0
48import Unity.Debug 0.1 as Debug
51StyledItem {
52 id: shell
54 theme.name: "Ubuntu.Components.Themes.SuruDark"
56 // to be set from outside
57 property int orientationAngle: 0
58 property int orientation
59 property Orientations orientations
60 property real nativeWidth
61 property real nativeHeight
62 property alias panelAreaShowProgress: panel.panelAreaShowProgress
63 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
64 property string mode: "full-greeter"
65 property alias oskEnabled: inputMethod.enabled
66 function updateFocusedAppOrientation() {
67 stage.updateFocusedAppOrientation();
68 }
69 function updateFocusedAppOrientationAnimated() {
70 stage.updateFocusedAppOrientationAnimated();
71 }
72 property bool hasMouse: false
73 property bool hasKeyboard: false
74 property bool hasTouchscreen: false
76 // to be read from outside
77 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
79 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
80 && stage.orientationChangesEnabled
81 && (!greeter || !greeter.animating)
83 readonly property bool showingGreeter: greeter && greeter.shown
85 property bool startingUp: true
86 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
88 property int supportedOrientations: {
89 if (startingUp) {
90 // Ensure we don't rotate during start up
91 return Qt.PrimaryOrientation;
92 } else if (showingGreeter || notifications.topmostIsFullscreen) {
93 return Qt.PrimaryOrientation;
94 } else {
95 return shell.orientations.map(stage.supportedOrientations);
96 }
97 }
99 readonly property var mainApp: stage.mainApp
101 onMainAppChanged: {
102 if (mainApp) {
103 _onMainAppChanged(mainApp.appId);
104 }
105 }
106 Connections {
107 target: ApplicationManager
108 onFocusRequested: {
109 if (shell.mainApp && shell.mainApp.appId === appId) {
110 _onMainAppChanged(appId);
111 }
112 }
113 }
114 function _onMainAppChanged(appId) {
115 if (wizard.active && appId != "" && appId != "unity8-dash") {
116 // If this happens on first boot, we may be in the
117 // wizard while receiving a call. But a call is more
118 // important than the wizard so just bail out of it.
119 wizard.hide();
120 }
122 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
123 // If we are in the middle of a call, make dialer lockedApp and show it.
124 // This can happen if user backs out of dialer back to greeter, then
125 // launches dialer again.
126 greeter.lockedApp = appId;
127 }
128 greeter.notifyAppFocusRequested(appId);
130 panel.indicators.hide();
131 launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
132 }
134 // For autopilot consumption
135 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
137 // Note when greeter is waiting on PAM, so that we can disable edges until
138 // we know which user data to show and whether the session is locked.
139 readonly property bool waitingOnGreeter: greeter && greeter.waiting
141 property real edgeSize: units.gu(settings.edgeDragWidth)
143 WallpaperResolver {
144 id: wallpaperResolver
145 objectName: "wallpaperResolver"
147 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
148 readonly property bool hasCustomBackground: background != defaultBackground
150 // Use a cached version of the scaled-down wallpaper (as sometimes the
151 // image can be quite big compared to the device size, including for
152 // our default wallpaper). We use a name=wallpaper argument here to
153 // make sure we don't litter our cache with lots of scaled images. We
154 // only need to bother caching one at a time.
155 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
157 GSettings {
158 id: backgroundSettings
159 schema.id: "org.gnome.desktop.background"
160 }
162 candidates: [
163 AccountsService.backgroundFile,
164 backgroundSettings.pictureUri,
165 defaultBackground
166 ]
167 }
169 readonly property alias greeter: greeterLoader.item
171 function activateApplication(appId) {
172 // Either open the app in our own session, or -- if we're acting as a
173 // greeter -- ask the user's session to open it for us.
174 if (shell.mode === "greeter") {
175 activateURL("application:///" + appId + ".desktop");
176 } else {
177 startApp(appId);
178 }
179 }
181 function activateURL(url) {
182 SessionBroadcast.requestUrlStart(AccountsService.user, url);
183 greeter.notifyUserRequestedApp();
184 panel.indicators.hide();
185 }
187 function startApp(appId) {
188 if (ApplicationManager.findApplication(appId)) {
189 ApplicationManager.requestFocusApplication(appId);
190 } else {
191 print("calling startApplication. active workspace is", WorkspaceManager.activeWorkspace)
192 ApplicationManager.startApplication(appId);
193 }
194 }
196 function startLockedApp(app) {
197 if (greeter.locked) {
198 greeter.lockedApp = app;
199 }
200 startApp(app); // locked apps are always in our same session
201 }
203 Binding {
204 target: LauncherModel
205 property: "applicationManager"
206 value: ApplicationManager
207 }
209 Component.onCompleted: {
210 finishStartUpTimer.start();
211 }
213 VolumeControl {
214 id: volumeControl
215 }
217 DashCommunicator {
218 id: dash
219 objectName: "dashCommunicator"
220 }
222 PhysicalKeysMapper {
223 id: physicalKeysMapper
224 objectName: "physicalKeysMapper"
226 onPowerKeyLongPressed: dialogs.showPowerDialog();
227 onVolumeDownTriggered: volumeControl.volumeDown();
228 onVolumeUpTriggered: volumeControl.volumeUp();
229 onScreenshotTriggered: itemGrabber.capture(shell);
230 }
232 GlobalShortcut {
233 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
234 }
236 WindowInputFilter {
237 id: inputFilter
238 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
239 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
240 }
242 WindowInputMonitor {
243 objectName: "windowInputMonitor"
244 onHomeKeyActivated: {
245 // Ignore when greeter is active, to avoid pocket presses
246 if (!greeter.active) {
247 launcher.fadeOut();
248 ApplicationManager.requestFocusApplication("unity8-dash");
249 }
250 }
251 onTouchBegun: { cursor.opacity = 0; }
252 onTouchEnded: {
253 // move the (hidden) cursor to the last known touch position
254 var mappedCoords = mapFromItem(null, pos.x, pos.y);
255 cursor.x = mappedCoords.x;
256 cursor.y = mappedCoords.y;
257 cursor.mouseNeverMoved = false;
258 }
259 }
261 GSettings {
262 id: settings
263 schema.id: "com.canonical.Unity8"
264 }
266 PanelState {
267 id: panelState
268 objectName: "panelState"
269 }
271 Item {
272 id: stages
273 objectName: "stages"
274 width: parent.width
275 height: parent.height
277 TopLevelWindowModel {
278 id: topLevelSurfaceList
279 objectName: "topLevelSurfaceList"
280 applicationManager: ApplicationManager // it's a singleton
281 surfaceManager: SurfaceManager
282 workspace: screenWindow.screen.currentWorkspace
283 }
285 Stage {
286 id: stage
287 objectName: "stage"
288 anchors.fill: parent
289 focus: true
291 dragAreaWidth: shell.edgeSize
292 background: wallpaperResolver.background
294 applicationManager: ApplicationManager
295 topLevelSurfaceList: topLevelSurfaceList
296 inputMethodRect: inputMethod.visibleRect
297 rightEdgePushProgress: rightEdgeBarrier.progress
299 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
300 ? "phone"
301 : shell.usageScenario
303 mode: usageScenario == "phone" ? "staged"
304 : usageScenario == "tablet" ? "stagedWithSideStage"
305 : "windowed"
307 shellOrientation: shell.orientation
308 shellOrientationAngle: shell.orientationAngle
309 orientations: shell.orientations
310 nativeWidth: shell.nativeWidth
311 nativeHeight: shell.nativeHeight
313 interactive: (!greeter || !greeter.shown)
314 && panel.indicators.fullyClosed
315 && !notifications.useModal
317 onInteractiveChanged: { if (interactive) { focus = true; } }
319 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
320 suspended: greeter.shown
321 altTabPressed: physicalKeysMapper.altTabPressed
322 oskEnabled: shell.oskEnabled
323 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
324 panelState: panelState
326 onSpreadShownChanged: {
327 panel.indicators.hide();
328 panel.applicationMenus.hide();
329 }
330 }
332 TouchGestureArea {
333 anchors.fill: stage
335 minimumTouchPoints: 4
336 maximumTouchPoints: minimumTouchPoints
338 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
339 touchPoints.length >= minimumTouchPoints &&
340 touchPoints.length <= maximumTouchPoints
341 property bool wasPressed: false
343 onRecognisedPressChanged: {
344 if (recognisedPress) {
345 wasPressed = true;
346 }
347 }
349 onStatusChanged: {
350 if (status !== TouchGestureArea.Recognized) {
351 if (status === TouchGestureArea.WaitingForTouch) {
352 if (wasPressed && !dragging) {
353 launcher.openDrawer(true);
354 }
355 }
356 wasPressed = false;
357 }
358 }
359 }
360 }
362 InputMethod {
363 id: inputMethod
364 objectName: "inputMethod"
365 surface: topLevelSurfaceList.inputMethodSurface
366 anchors {
367 fill: parent
368 topMargin: panel.panelHeight
369 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
370 }
371 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
372 }
374 Loader {
375 id: greeterLoader
376 objectName: "greeterLoader"
377 anchors.fill: parent
378 anchors.topMargin: panel.panelHeight
379 sourceComponent: {
380 if (shell.mode != "shell") {
381 if (screenWindow.primary) return integratedGreeter;
382 return secondaryGreeter;
383 }
384 return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
385 }
386 onLoaded: {
387 item.objectName = "greeter"
388 }
389 property bool openDrawerAfterUnlock: false
390 Connections {
391 target: greeter
392 onActiveChanged: {
393 if (!greeter.active && greeterLoader.openDrawerAfterUnlock) {
394 launcher.openDrawer(false);
395 greeterLoader.openDrawerAfterUnlock = false;
396 }
397 }
398 }
399 }
401 Component {
402 id: integratedGreeter
403 Greeter {
405 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
406 hides: [launcher, panel.indicators, panel.applicationMenus]
407 tabletMode: shell.usageScenario != "phone"
408 forcedUnlock: wizard.active || shell.mode === "full-shell"
409 background: wallpaperResolver.cachedBackground
410 hasCustomBackground: wallpaperResolver.hasCustomBackground
411 allowFingerprint: !dialogs.hasActiveDialog &&
412 !notifications.topmostIsFullscreen &&
413 !panel.indicators.shown
415 // avoid overlapping with Launcher's edge drag area
416 // FIXME: Fix TouchRegistry & friends and remove this workaround
417 // Issue involves launcher's DDA getting disabled on a long
418 // left-edge drag
419 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
421 onSessionStarted: {
422 launcher.hide();
423 }
425 onTease: {
426 if (!tutorial.running) {
427 launcher.tease();
428 }
429 }
431 onEmergencyCall: startLockedApp("dialer-app")
432 }
433 }
435 Component {
436 id: secondaryGreeter
437 SecondaryGreeter {
438 hides: [launcher, panel.indicators]
439 }
440 }
442 Timer {
443 // See powerConnection for why this is useful
444 id: showGreeterDelayed
445 interval: 1
446 onTriggered: {
447 greeter.forceShow();
448 }
449 }
451 Connections {
452 id: callConnection
453 target: callManager
455 onHasCallsChanged: {
456 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
457 // We just received an incoming call while locked. The
458 // indicator will have already launched dialer-app for us, but
459 // there is a race between "hasCalls" changing and the dialer
460 // starting up. So in case we lose that race, we'll start/
461 // focus the dialer ourselves here too. Even if the indicator
462 // didn't launch the dialer for some reason (or maybe a call
463 // started via some other means), if an active call is
464 // happening, we want to be in the dialer.
465 startLockedApp("dialer-app")
466 }
467 }
468 }
470 Connections {
471 id: powerConnection
472 target: Powerd
474 onStatusChanged: {
475 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
476 !callManager.hasCalls && !wizard.active) {
477 // We don't want to simply call greeter.showNow() here, because
478 // that will take too long. Qt will delay button event
479 // handling until the greeter is done loading and may think the
480 // user held down the power button the whole time, leading to a
481 // power dialog being shown. Instead, delay showing the
482 // greeter until we've finished handling the event. We could
483 // make the greeter load asynchronously instead, but that
484 // introduces a whole host of timing issues, especially with
485 // its animations. So this is simpler.
486 showGreeterDelayed.start();
487 }
488 }
489 }
491 function showHome() {
492 greeter.notifyUserRequestedApp();
494 if (shell.mode === "greeter") {
495 SessionBroadcast.requestHomeShown(AccountsService.user);
496 } else {
497 if (!greeter.active) {
498 launcher.openDrawer(false);
499 } else {
500 greeterLoader.openDrawerAfterUnlock = true;
501 }
502 }
503 }
505 Item {
506 id: overlay
507 z: 10
509 anchors.fill: parent
511 Panel {
512 id: panel
513 objectName: "panel"
514 anchors.fill: parent //because this draws indicator menus
516 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
517 minimizedPanelHeight: units.gu(3)
518 expandedPanelHeight: units.gu(7)
519 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
520 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
522 indicators {
523 hides: [launcher]
524 available: tutorial.panelEnabled
525 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
526 && (!greeter || !greeter.hasLockedApp)
527 && !shell.waitingOnGreeter
528 && settings.enableIndicatorMenu
530 model: Indicators.IndicatorsModel {
531 // tablet and phone both use the same profile
532 // FIXME: use just "phone" for greeter too, but first fix
533 // greeter app launching to either load the app inside the
534 // greeter or tell the session to load the app. This will
535 // involve taking the url-dispatcher dbus name and using
536 // SessionBroadcast to tell the session.
537 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
538 Component.onCompleted: load();
539 }
540 }
542 applicationMenus {
543 hides: [launcher]
544 available: (!greeter || !greeter.shown)
545 && !shell.waitingOnGreeter
546 && !stage.spreadShown
547 }
549 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
550 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
551 : false
552 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
553 || greeter.hasLockedApp
554 greeterShown: greeter && greeter.shown
555 hasKeyboard: shell.hasKeyboard
556 panelState: panelState
557 }
559 Launcher {
560 id: launcher
561 objectName: "launcher"
563 anchors.top: parent.top
564 anchors.topMargin: inverted ? 0 : panel.panelHeight
565 anchors.bottom: parent.bottom
566 width: parent.width
567 dragAreaWidth: shell.edgeSize
568 available: tutorial.launcherEnabled
569 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
570 && !greeter.hasLockedApp
571 && !shell.waitingOnGreeter
572 && settings.enableLauncher
573 inverted: shell.usageScenario !== "desktop"
574 superPressed: physicalKeysMapper.superPressed
575 superTabPressed: physicalKeysMapper.superTabPressed
576 panelWidth: units.gu(settings.launcherWidth)
577 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
578 blurSource: greeter.shown ? greeter : stages
579 topPanelHeight: panel.panelHeight
580 drawerEnabled: !greeter.active
582 onShowDashHome: showHome()
583 onLauncherApplicationSelected: {
584 greeter.notifyUserRequestedApp();
585 shell.activateApplication(appId);
586 }
587 onShownChanged: {
588 if (shown) {
589 panel.indicators.hide()
590 }
591 }
592 onFocusChanged: {
593 if (!focus) {
594 stage.focus = true;
595 }
596 }
598 GlobalShortcut {
599 shortcut: Qt.MetaModifier | Qt.Key_A
600 onTriggered: {
601 launcher.openDrawer(true);
602 }
603 }
604 GlobalShortcut {
605 shortcut: Qt.AltModifier | Qt.Key_F1
606 onTriggered: {
607 launcher.openForKeyboardNavigation();
608 }
609 }
610 GlobalShortcut {
611 shortcut: Qt.MetaModifier | Qt.Key_0
612 onTriggered: {
613 if (LauncherModel.get(9)) {
614 activateApplication(LauncherModel.get(9).appId);
615 }
616 }
617 }
618 Repeater {
619 model: 9
620 GlobalShortcut {
621 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
622 onTriggered: {
623 if (LauncherModel.get(index)) {
624 activateApplication(LauncherModel.get(index).appId);
625 }
626 }
627 }
628 }
629 }
631 KeyboardShortcutsOverlay {
632 objectName: "shortcutsOverlay"
633 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
634 && height < parent.height - padding - panel.panelHeight
635 anchors.centerIn: parent
636 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
637 anchors.verticalCenterOffset: panel.panelHeight/2
638 visible: opacity > 0
639 opacity: enabled ? 0.95 : 0
641 Behavior on opacity {
642 UbuntuNumberAnimation {}
643 }
644 }
646 Tutorial {
647 id: tutorial
648 objectName: "tutorial"
649 anchors.fill: parent
651 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
652 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
653 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
654 inputMethod.visible ||
655 (launcher.shown && !launcher.lockedVisible) ||
656 panel.indicators.shown || stage.rightEdgeDragProgress > 0
657 usageScenario: shell.usageScenario
658 lastInputTimestamp: inputFilter.lastInputTimestamp
659 launcher: launcher
660 panel: panel
661 stage: stage
662 }
664 Wizard {
665 id: wizard
666 objectName: "wizard"
667 anchors.fill: parent
668 deferred: shell.mode === "greeter"
670 function unlockWhenDoneWithWizard() {
671 if (!active) {
672 Connectivity.unlockAllModems();
673 }
674 }
676 Component.onCompleted: unlockWhenDoneWithWizard()
677 onActiveChanged: unlockWhenDoneWithWizard()
678 }
680 MouseArea { // modal notifications prevent interacting with other contents
681 anchors.fill: parent
682 visible: notifications.useModal
683 enabled: visible
684 }
686 Notifications {
687 id: notifications
689 model: NotificationBackend.Model
690 margin: units.gu(1)
691 hasMouse: shell.hasMouse
692 background: wallpaperResolver.cachedBackground
694 y: topmostIsFullscreen ? 0 : panel.panelHeight
695 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
697 states: [
698 State {
699 name: "narrow"
700 when: overlay.width <= units.gu(60)
701 AnchorChanges {
702 target: notifications
703 anchors.left: parent.left
704 anchors.right: parent.right
705 }
706 },
707 State {
708 name: "wide"
709 when: overlay.width > units.gu(60)
710 AnchorChanges {
711 target: notifications
712 anchors.left: undefined
713 anchors.right: parent.right
714 }
715 PropertyChanges { target: notifications; width: units.gu(38) }
716 }
717 ]
718 }
720 EdgeBarrier {
721 id: rightEdgeBarrier
722 enabled: !greeter.shown
724 // NB: it does its own positioning according to the specified edge
725 edge: Qt.RightEdge
727 onPassed: {
728 panel.indicators.hide()
729 }
731 material: Component {
732 Item {
733 Rectangle {
734 width: parent.height
735 height: parent.width
736 rotation: 90
737 anchors.centerIn: parent
738 gradient: Gradient {
739 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
740 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
741 }
742 }
743 }
744 }
745 }
746 }
748 Dialogs {
749 id: dialogs
750 objectName: "dialogs"
751 anchors.fill: parent
752 visible: hasActiveDialog
753 z: overlay.z + 10
754 usageScenario: shell.usageScenario
755 hasKeyboard: shell.hasKeyboard
756 onPowerOffClicked: {
757 shutdownFadeOutRectangle.enabled = true;
758 shutdownFadeOutRectangle.visible = true;
759 shutdownFadeOut.start();
760 }
761 }
763 Connections {
764 target: SessionBroadcast
765 onShowHome: if (shell.mode !== "greeter") showHome()
766 }
768 URLDispatcher {
769 id: urlDispatcher
770 objectName: "urlDispatcher"
771 active: shell.mode === "greeter"
772 onUrlRequested: shell.activateURL(url)
773 }
775 ItemGrabber {
776 id: itemGrabber
777 anchors.fill: parent
778 z: dialogs.z + 10
779 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
780 Connections {
781 target: stage
782 ignoreUnknownSignals: true
783 onItemSnapshotRequested: itemGrabber.capture(item)
784 }
785 }
787 Timer {
788 id: cursorHidingTimer
789 interval: 3000
790 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
791 onTriggered: cursor.opacity = 0;
792 }
794 Cursor {
795 id: cursor
796 objectName: "Pointer-"+screenWindow.objectName
798 z: itemGrabber.z + 1
799 opacity: 0
800 topBoundaryOffset: panel.panelHeight
801 enabled: shell.hasMouse && screenWindow.active
802 visible: enabled
804 property bool mouseNeverMoved: true
805 Binding {
806 target: cursor; property: "x"; value: shell.width / 2
807 when: cursor.mouseNeverMoved && cursor.visible
808 }
809 Binding {
810 target: cursor; property: "y"; value: shell.height / 2
811 when: cursor.mouseNeverMoved && cursor.visible
812 }
814 confiningItem: stage.itemConfiningMouseCursor
816 height: units.gu(3)
818 readonly property var previewRectangle: stage.previewRectangle.target &&
819 stage.previewRectangle.target.dragging ?
820 stage.previewRectangle : null
822 onPushedLeftBoundary: {
823 if (buttons === Qt.NoButton) {
824 launcher.pushEdge(amount);
825 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
826 previewRectangle.maximizeLeft(amount);
827 }
828 }
830 onPushedRightBoundary: {
831 if (buttons === Qt.NoButton) {
832 rightEdgeBarrier.push(amount);
833 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
834 previewRectangle.maximizeRight(amount);
835 }
836 }
838 onPushedTopBoundary: {
839 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
840 previewRectangle.maximize(amount);
841 }
842 }
843 onPushedTopLeftCorner: {
844 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
845 previewRectangle.maximizeTopLeft(amount);
846 }
847 }
848 onPushedTopRightCorner: {
849 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
850 previewRectangle.maximizeTopRight(amount);
851 }
852 }
853 onPushedBottomLeftCorner: {
854 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
855 previewRectangle.maximizeBottomLeft(amount);
856 }
857 }
858 onPushedBottomRightCorner: {
859 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
860 previewRectangle.maximizeBottomRight(amount);
861 }
862 }
863 onPushStopped: {
864 if (previewRectangle) {
865 previewRectangle.stop();
866 }
867 }
869 onMouseMoved: {
870 mouseNeverMoved = false;
871 cursor.opacity = 1;
872 }
874 Behavior on opacity { UbuntuNumberAnimation {} }
875 }
877 // non-visual objects
878 KeymapSwitcher {
879 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
880 }
881 BrightnessControl {}
883 Rectangle {
884 id: shutdownFadeOutRectangle
885 z: cursor.z + 1
886 enabled: false
887 visible: false
888 color: "black"
889 anchors.fill: parent
890 opacity: 0.0
891 NumberAnimation on opacity {
892 id: shutdownFadeOut
893 from: 0.0
894 to: 1.0
895 onStopped: {
896 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
897 DBusUnitySessionService.shutdown();
898 }
899 }
900 }
901 }
903 Loader {
904 z: shutdownFadeOutRectangle.z + 1
905 active: DebuggingController.logOverlay
907 sourceComponent: Text {
908 width: shell.width
909 height: shell.height
911 text: consoleLog.out
912 Debug.ConsoleLog {
913 id: consoleLog
914 enabled: true
915 }
916 }
917 }