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