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