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