on 792e2c9 help sign in
unity8/qml/Shell.qml
885 lines 31 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 alias surfaceManager: topLevelSurfaceList.surfaceManager
58 property int orientationAngle: 0
59 property int orientation
60 property Orientations orientations
61 property real nativeWidth
62 property real nativeHeight
63 property alias panelAreaShowProgress: panel.panelAreaShowProgress
64 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
65 property string mode: "full-greeter"
66 property alias oskEnabled: inputMethod.enabled
67 function updateFocusedAppOrientation() {
68 stage.updateFocusedAppOrientation();
69 }
70 function updateFocusedAppOrientationAnimated() {
71 stage.updateFocusedAppOrientationAnimated();
72 }
73 property bool hasMouse: false
74 property bool hasKeyboard: 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 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 }
282 Stage {
283 id: stage
284 objectName: "stage"
285 anchors.fill: parent
286 focus: true
288 dragAreaWidth: shell.edgeSize
289 background: wallpaperResolver.background
291 applicationManager: ApplicationManager
292 topLevelSurfaceList: topLevelSurfaceList
293 inputMethodRect: inputMethod.visibleRect
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: 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 }
544 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
545 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
546 : false
547 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
548 || greeter.hasLockedApp
549 greeterShown: greeter && greeter.shown
550 panelState: panelState
551 }
553 Launcher {
554 id: launcher
555 objectName: "launcher"
557 anchors.top: parent.top
558 anchors.topMargin: inverted ? 0 : panel.panelHeight
559 anchors.bottom: parent.bottom
560 width: parent.width
561 dragAreaWidth: shell.edgeSize
562 available: tutorial.launcherEnabled
563 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
564 && !greeter.hasLockedApp
565 && !shell.waitingOnGreeter
566 && settings.enableLauncher
567 inverted: shell.usageScenario !== "desktop"
568 superPressed: physicalKeysMapper.superPressed
569 superTabPressed: physicalKeysMapper.superTabPressed
570 panelWidth: units.gu(settings.launcherWidth)
571 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
572 blurSource: greeter.shown ? greeter : stages
573 topPanelHeight: panel.panelHeight
574 drawerEnabled: !greeter.active
576 onShowDashHome: showHome()
577 onLauncherApplicationSelected: {
578 greeter.notifyUserRequestedApp();
579 shell.activateApplication(appId);
580 }
581 onShownChanged: {
582 if (shown) {
583 panel.indicators.hide()
584 }
585 }
586 onFocusChanged: {
587 if (!focus) {
588 stage.focus = true;
589 }
590 }
592 GlobalShortcut {
593 shortcut: Qt.MetaModifier | Qt.Key_A
594 onTriggered: {
595 launcher.openDrawer(true);
596 }
597 }
598 GlobalShortcut {
599 shortcut: Qt.AltModifier | Qt.Key_F1
600 onTriggered: {
601 launcher.openForKeyboardNavigation();
602 }
603 }
604 GlobalShortcut {
605 shortcut: Qt.MetaModifier | Qt.Key_0
606 onTriggered: {
607 if (LauncherModel.get(9)) {
608 activateApplication(LauncherModel.get(9).appId);
609 }
610 }
611 }
612 Repeater {
613 model: 9
614 GlobalShortcut {
615 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
616 onTriggered: {
617 if (LauncherModel.get(index)) {
618 activateApplication(LauncherModel.get(index).appId);
619 }
620 }
621 }
622 }
623 }
625 KeyboardShortcutsOverlay {
626 objectName: "shortcutsOverlay"
627 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
628 && height < parent.height - padding - panel.panelHeight
629 anchors.centerIn: parent
630 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
631 anchors.verticalCenterOffset: panel.panelHeight/2
632 visible: opacity > 0
633 opacity: enabled ? 0.95 : 0
635 Behavior on opacity {
636 UbuntuNumberAnimation {}
637 }
638 }
640 Tutorial {
641 id: tutorial
642 objectName: "tutorial"
643 anchors.fill: parent
645 paused: callManager.hasCalls || !greeter || greeter.active ||
646 wizard.active
647 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
648 inputMethod.visible ||
649 (launcher.shown && !launcher.lockedVisible) ||
650 panel.indicators.shown || stage.rightEdgeDragProgress > 0
651 usageScenario: shell.usageScenario
652 lastInputTimestamp: inputFilter.lastInputTimestamp
653 launcher: launcher
654 panel: panel
655 stage: stage
656 }
658 Wizard {
659 id: wizard
660 objectName: "wizard"
661 anchors.fill: parent
662 deferred: shell.mode === "greeter"
664 function unlockWhenDoneWithWizard() {
665 if (!active) {
666 Connectivity.unlockAllModems();
667 }
668 }
670 Component.onCompleted: unlockWhenDoneWithWizard()
671 onActiveChanged: unlockWhenDoneWithWizard()
672 }
674 MouseArea { // modal notifications prevent interacting with other contents
675 anchors.fill: parent
676 visible: notifications.useModal
677 enabled: visible
678 }
680 Notifications {
681 id: notifications
683 model: NotificationBackend.Model
684 margin: units.gu(1)
685 hasMouse: shell.hasMouse
686 background: wallpaperResolver.cachedBackground
688 y: topmostIsFullscreen ? 0 : panel.panelHeight
689 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
691 states: [
692 State {
693 name: "narrow"
694 when: overlay.width <= units.gu(60)
695 AnchorChanges {
696 target: notifications
697 anchors.left: parent.left
698 anchors.right: parent.right
699 }
700 },
701 State {
702 name: "wide"
703 when: overlay.width > units.gu(60)
704 AnchorChanges {
705 target: notifications
706 anchors.left: undefined
707 anchors.right: parent.right
708 }
709 PropertyChanges { target: notifications; width: units.gu(38) }
710 }
711 ]
712 }
713 }
715 Dialogs {
716 id: dialogs
717 objectName: "dialogs"
718 anchors.fill: parent
719 visible: hasActiveDialog
720 z: overlay.z + 10
721 usageScenario: shell.usageScenario
722 hasKeyboard: shell.hasKeyboard
723 onPowerOffClicked: {
724 shutdownFadeOutRectangle.enabled = true;
725 shutdownFadeOutRectangle.visible = true;
726 shutdownFadeOut.start();
727 }
728 }
730 Connections {
731 target: SessionBroadcast
732 onShowHome: if (shell.mode !== "greeter") showHome()
733 }
735 URLDispatcher {
736 id: urlDispatcher
737 objectName: "urlDispatcher"
738 active: shell.mode === "greeter"
739 onUrlRequested: shell.activateURL(url)
740 }
742 ItemGrabber {
743 id: itemGrabber
744 anchors.fill: parent
745 z: dialogs.z + 10
746 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
747 Connections {
748 target: stage
749 ignoreUnknownSignals: true
750 onItemSnapshotRequested: itemGrabber.capture(item)
751 }
752 }
754 Timer {
755 id: cursorHidingTimer
756 interval: 3000
757 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
758 onTriggered: cursor.opacity = 0;
759 }
761 Cursor {
762 id: cursor
763 objectName: "Pointer-"+screenWindow.objectName
765 z: itemGrabber.z + 1
766 opacity: 0
767 topBoundaryOffset: panel.panelHeight
768 enabled: shell.hasMouse && screenWindow.active
769 visible: enabled
771 property bool mouseNeverMoved: true
772 Binding {
773 target: cursor; property: "x"; value: shell.width / 2
774 when: cursor.mouseNeverMoved && cursor.visible
775 }
776 Binding {
777 target: cursor; property: "y"; value: shell.height / 2
778 when: cursor.mouseNeverMoved && cursor.visible
779 }
781 confiningItem: stage.itemConfiningMouseCursor
783 height: units.gu(3)
785 readonly property var previewRectangle: stage.previewRectangle.target &&
786 stage.previewRectangle.target.dragging ?
787 stage.previewRectangle : null
789 onPushedLeftBoundary: {
790 if (buttons === Qt.NoButton) {
791 launcher.pushEdge(amount);
792 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
793 previewRectangle.maximizeLeft(amount);
794 }
795 }
797 onPushedRightBoundary: {
798 if (buttons === Qt.NoButton) {
799 stage.pushRightEdge(amount);
800 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
801 previewRectangle.maximizeRight(amount);
802 }
803 }
805 onPushedTopBoundary: {
806 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
807 previewRectangle.maximize(amount);
808 }
809 }
810 onPushedTopLeftCorner: {
811 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
812 previewRectangle.maximizeTopLeft(amount);
813 }
814 }
815 onPushedTopRightCorner: {
816 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
817 previewRectangle.maximizeTopRight(amount);
818 }
819 }
820 onPushedBottomLeftCorner: {
821 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
822 previewRectangle.maximizeBottomLeft(amount);
823 }
824 }
825 onPushedBottomRightCorner: {
826 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
827 previewRectangle.maximizeBottomRight(amount);
828 }
829 }
830 onPushStopped: {
831 if (previewRectangle) {
832 previewRectangle.stop();
833 }
834 }
836 onMouseMoved: {
837 mouseNeverMoved = false;
838 cursor.opacity = 1;
839 }
841 Behavior on opacity { UbuntuNumberAnimation {} }
842 }
844 // non-visual objects
845 KeymapSwitcher {
846 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
847 }
848 BrightnessControl {}
850 Rectangle {
851 id: shutdownFadeOutRectangle
852 z: cursor.z + 1
853 enabled: false
854 visible: false
855 color: "black"
856 anchors.fill: parent
857 opacity: 0.0
858 NumberAnimation on opacity {
859 id: shutdownFadeOut
860 from: 0.0
861 to: 1.0
862 onStopped: {
863 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
864 DBusUnitySessionService.shutdown();
865 }
866 }
867 }
868 }
870 Loader {
871 z: shutdownFadeOutRectangle.z + 1
872 active: DebuggingController.logOverlay
874 sourceComponent: Text {
875 width: shell.width
876 height: shell.height
878 text: consoleLog.out
879 Debug.ConsoleLog {
880 id: consoleLog
881 enabled: true
882 }
883 }
884 }