on 8.15+17.04.20170321-0ubuntu1 help sign in
unity8/qml/Shell.qml
891 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 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 != "" && appId != "unity8-dash") {
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.fadeOut();
245 ApplicationManager.requestFocusApplication("unity8-dash");
246 }
247 }
248 onTouchBegun: { cursor.opacity = 0; }
249 onTouchEnded: {
250 // move the (hidden) cursor to the last known touch position
251 var mappedCoords = mapFromItem(null, pos.x, pos.y);
252 cursor.x = mappedCoords.x;
253 cursor.y = mappedCoords.y;
254 cursor.mouseNeverMoved = false;
255 }
256 }
258 GSettings {
259 id: settings
260 schema.id: "com.canonical.Unity8"
261 }
263 Item {
264 id: stages
265 objectName: "stages"
266 width: parent.width
267 height: parent.height
269 SurfaceManager {
270 id: surfaceMan
271 objectName: "surfaceManager"
272 }
273 TopLevelWindowModel {
274 id: topLevelSurfaceList
275 objectName: "topLevelSurfaceList"
276 applicationManager: ApplicationManager // it's a singleton
277 surfaceManager: surfaceMan
278 }
280 Stage {
281 id: stage
282 objectName: "stage"
283 anchors.fill: parent
284 focus: true
286 dragAreaWidth: shell.edgeSize
287 background: wallpaperResolver.background
289 applicationManager: ApplicationManager
290 topLevelSurfaceList: topLevelSurfaceList
291 inputMethodRect: inputMethod.visibleRect
292 rightEdgePushProgress: rightEdgeBarrier.progress
294 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
295 ? "phone"
296 : shell.usageScenario
298 mode: usageScenario == "phone" ? "staged"
299 : usageScenario == "tablet" ? "stagedWithSideStage"
300 : "windowed"
302 shellOrientation: shell.orientation
303 shellOrientationAngle: shell.orientationAngle
304 orientations: shell.orientations
305 nativeWidth: shell.nativeWidth
306 nativeHeight: shell.nativeHeight
308 interactive: (!greeter || !greeter.shown)
309 && panel.indicators.fullyClosed
310 && !notifications.useModal
312 onInteractiveChanged: { if (interactive) { focus = true; } }
314 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
315 suspended: greeter.shown
316 altTabPressed: physicalKeysMapper.altTabPressed
317 oskEnabled: shell.oskEnabled
318 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
320 onSpreadShownChanged: {
321 panel.indicators.hide();
322 panel.applicationMenus.hide();
323 }
324 }
326 TouchGestureArea {
327 anchors.fill: stage
329 minimumTouchPoints: 4
330 maximumTouchPoints: minimumTouchPoints
332 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
333 touchPoints.length >= minimumTouchPoints &&
334 touchPoints.length <= maximumTouchPoints
335 property bool wasPressed: false
337 onRecognisedPressChanged: {
338 if (recognisedPress) {
339 wasPressed = true;
340 }
341 }
343 onStatusChanged: {
344 if (status !== TouchGestureArea.Recognized) {
345 if (status === TouchGestureArea.WaitingForTouch) {
346 if (wasPressed && !dragging) {
347 launcher.openDrawer(true);
348 }
349 }
350 wasPressed = false;
351 }
352 }
353 }
354 }
356 InputMethod {
357 id: inputMethod
358 objectName: "inputMethod"
359 surface: topLevelSurfaceList.inputMethodSurface
360 anchors {
361 fill: parent
362 topMargin: panel.panelHeight
363 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
364 }
365 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
366 }
368 Loader {
369 id: greeterLoader
370 objectName: "greeterLoader"
371 anchors.fill: parent
372 anchors.topMargin: panel.panelHeight
373 sourceComponent: shell.mode != "shell" ? integratedGreeter :
374 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
375 onLoaded: {
376 item.objectName = "greeter"
377 }
378 property bool openDrawerAfterUnlock: false
379 Connections {
380 target: greeter
381 onActiveChanged: {
382 if (!greeter.active && greeterLoader.openDrawerAfterUnlock) {
383 launcher.openDrawer(false);
384 greeterLoader.openDrawerAfterUnlock = false;
385 }
386 }
387 }
388 }
390 Component {
391 id: integratedGreeter
392 Greeter {
394 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
395 hides: [launcher, panel.indicators, panel.applicationMenus]
396 tabletMode: shell.usageScenario != "phone"
397 forcedUnlock: wizard.active || shell.mode === "full-shell"
398 background: wallpaperResolver.cachedBackground
399 hasCustomBackground: wallpaperResolver.hasCustomBackground
400 allowFingerprint: !dialogs.hasActiveDialog &&
401 !notifications.topmostIsFullscreen &&
402 !panel.indicators.shown
404 // avoid overlapping with Launcher's edge drag area
405 // FIXME: Fix TouchRegistry & friends and remove this workaround
406 // Issue involves launcher's DDA getting disabled on a long
407 // left-edge drag
408 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
410 onSessionStarted: {
411 launcher.hide();
412 }
414 onTease: {
415 if (!tutorial.running) {
416 launcher.tease();
417 }
418 }
420 onEmergencyCall: startLockedApp("dialer-app")
421 }
422 }
424 Timer {
425 // See powerConnection for why this is useful
426 id: showGreeterDelayed
427 interval: 1
428 onTriggered: {
429 // Go through the dbus service, because it has checks for whether
430 // we are even allowed to lock or not.
431 DBusUnitySessionService.PromptLock();
432 }
433 }
435 Connections {
436 id: callConnection
437 target: callManager
439 onHasCallsChanged: {
440 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
441 // We just received an incoming call while locked. The
442 // indicator will have already launched dialer-app for us, but
443 // there is a race between "hasCalls" changing and the dialer
444 // starting up. So in case we lose that race, we'll start/
445 // focus the dialer ourselves here too. Even if the indicator
446 // didn't launch the dialer for some reason (or maybe a call
447 // started via some other means), if an active call is
448 // happening, we want to be in the dialer.
449 startLockedApp("dialer-app")
450 }
451 }
452 }
454 Connections {
455 id: powerConnection
456 target: Powerd
458 onStatusChanged: {
459 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
460 !callManager.hasCalls && !wizard.active) {
461 // We don't want to simply call greeter.showNow() here, because
462 // that will take too long. Qt will delay button event
463 // handling until the greeter is done loading and may think the
464 // user held down the power button the whole time, leading to a
465 // power dialog being shown. Instead, delay showing the
466 // greeter until we've finished handling the event. We could
467 // make the greeter load asynchronously instead, but that
468 // introduces a whole host of timing issues, especially with
469 // its animations. So this is simpler.
470 showGreeterDelayed.start();
471 }
472 }
473 }
475 function showHome() {
476 greeter.notifyUserRequestedApp();
478 if (shell.mode === "greeter") {
479 SessionBroadcast.requestHomeShown(AccountsService.user);
480 } else {
481 if (!greeter.active) {
482 launcher.openDrawer(false);
483 } else {
484 greeterLoader.openDrawerAfterUnlock = true;
485 }
486 }
487 }
489 Item {
490 id: overlay
491 z: 10
493 anchors.fill: parent
495 Panel {
496 id: panel
497 objectName: "panel"
498 anchors.fill: parent //because this draws indicator menus
500 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
501 minimizedPanelHeight: units.gu(3)
502 expandedPanelHeight: units.gu(7)
503 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
504 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
505 applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
507 indicators {
508 hides: [launcher]
509 available: tutorial.panelEnabled
510 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
511 && (!greeter || !greeter.hasLockedApp)
512 && !shell.waitingOnGreeter
513 && settings.enableIndicatorMenu
515 model: Indicators.IndicatorsModel {
516 // tablet and phone both use the same profile
517 // FIXME: use just "phone" for greeter too, but first fix
518 // greeter app launching to either load the app inside the
519 // greeter or tell the session to load the app. This will
520 // involve taking the url-dispatcher dbus name and using
521 // SessionBroadcast to tell the session.
522 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
523 Component.onCompleted: load();
524 }
525 }
527 applicationMenus {
528 hides: [launcher]
529 available: (!greeter || !greeter.shown)
530 && !shell.waitingOnGreeter
531 && !stage.spreadShown
532 }
534 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
535 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
536 : false
537 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
538 || greeter.hasLockedApp
539 greeterShown: greeter && greeter.shown
540 hasKeyboard: shell.hasKeyboard
541 }
543 Launcher {
544 id: launcher
545 objectName: "launcher"
547 anchors.top: parent.top
548 anchors.topMargin: inverted ? 0 : panel.panelHeight
549 anchors.bottom: parent.bottom
550 width: parent.width
551 dragAreaWidth: shell.edgeSize
552 available: tutorial.launcherEnabled
553 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
554 && !greeter.hasLockedApp
555 && !shell.waitingOnGreeter
556 && settings.enableLauncher
557 inverted: shell.usageScenario !== "desktop"
558 superPressed: physicalKeysMapper.superPressed
559 superTabPressed: physicalKeysMapper.superTabPressed
560 panelWidth: units.gu(settings.launcherWidth)
561 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
562 blurSource: greeter.shown ? greeter : stages
563 topPanelHeight: panel.panelHeight
564 drawerEnabled: !greeter.active
565 privateMode: greeter.active
567 onShowDashHome: showHome()
568 onLauncherApplicationSelected: {
569 greeter.notifyUserRequestedApp();
570 shell.activateApplication(appId);
571 }
572 onShownChanged: {
573 if (shown) {
574 panel.indicators.hide();
575 panel.applicationMenus.hide();
576 }
577 }
578 onDrawerShownChanged: {
579 if (drawerShown) {
580 panel.indicators.hide();
581 panel.applicationMenus.hide();
582 }
583 }
584 onFocusChanged: {
585 if (!focus) {
586 stage.focus = true;
587 }
588 }
590 GlobalShortcut {
591 shortcut: Qt.MetaModifier | Qt.Key_A
592 onTriggered: {
593 launcher.openDrawer(true);
594 }
595 }
596 GlobalShortcut {
597 shortcut: Qt.AltModifier | Qt.Key_F1
598 onTriggered: {
599 launcher.openForKeyboardNavigation();
600 }
601 }
602 GlobalShortcut {
603 shortcut: Qt.MetaModifier | Qt.Key_0
604 onTriggered: {
605 if (LauncherModel.get(9)) {
606 activateApplication(LauncherModel.get(9).appId);
607 }
608 }
609 }
610 Repeater {
611 model: 9
612 GlobalShortcut {
613 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
614 onTriggered: {
615 if (LauncherModel.get(index)) {
616 activateApplication(LauncherModel.get(index).appId);
617 }
618 }
619 }
620 }
621 }
623 KeyboardShortcutsOverlay {
624 objectName: "shortcutsOverlay"
625 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
626 && height < parent.height - padding - panel.panelHeight
627 anchors.centerIn: parent
628 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
629 anchors.verticalCenterOffset: panel.panelHeight/2
630 visible: opacity > 0
631 opacity: enabled ? 0.95 : 0
633 Behavior on opacity {
634 UbuntuNumberAnimation {}
635 }
636 }
638 Tutorial {
639 id: tutorial
640 objectName: "tutorial"
641 anchors.fill: parent
643 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
644 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
645 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
646 inputMethod.visible ||
647 (launcher.shown && !launcher.lockedVisible) ||
648 panel.indicators.shown || stage.rightEdgeDragProgress > 0
649 usageScenario: shell.usageScenario
650 lastInputTimestamp: inputFilter.lastInputTimestamp
651 launcher: launcher
652 panel: panel
653 stage: stage
654 }
656 Wizard {
657 id: wizard
658 objectName: "wizard"
659 anchors.fill: parent
660 deferred: shell.mode === "greeter"
662 function unlockWhenDoneWithWizard() {
663 if (!active) {
664 Connectivity.unlockAllModems();
665 }
666 }
668 Component.onCompleted: unlockWhenDoneWithWizard()
669 onActiveChanged: unlockWhenDoneWithWizard()
670 }
672 MouseArea { // modal notifications prevent interacting with other contents
673 anchors.fill: parent
674 visible: notifications.useModal
675 enabled: visible
676 }
678 Notifications {
679 id: notifications
681 model: NotificationBackend.Model
682 margin: units.gu(1)
683 hasMouse: shell.hasMouse
684 background: wallpaperResolver.cachedBackground
686 y: topmostIsFullscreen ? 0 : panel.panelHeight
687 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
689 states: [
690 State {
691 name: "narrow"
692 when: overlay.width <= units.gu(60)
693 AnchorChanges {
694 target: notifications
695 anchors.left: parent.left
696 anchors.right: parent.right
697 }
698 },
699 State {
700 name: "wide"
701 when: overlay.width > units.gu(60)
702 AnchorChanges {
703 target: notifications
704 anchors.left: undefined
705 anchors.right: parent.right
706 }
707 PropertyChanges { target: notifications; width: units.gu(38) }
708 }
709 ]
710 }
712 EdgeBarrier {
713 id: rightEdgeBarrier
714 enabled: !greeter.shown
716 // NB: it does its own positioning according to the specified edge
717 edge: Qt.RightEdge
719 onPassed: {
720 panel.indicators.hide()
721 }
723 material: Component {
724 Item {
725 Rectangle {
726 width: parent.height
727 height: parent.width
728 rotation: 90
729 anchors.centerIn: parent
730 gradient: Gradient {
731 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
732 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
733 }
734 }
735 }
736 }
737 }
738 }
740 Dialogs {
741 id: dialogs
742 objectName: "dialogs"
743 anchors.fill: parent
744 visible: hasActiveDialog
745 z: overlay.z + 10
746 usageScenario: shell.usageScenario
747 hasKeyboard: shell.hasKeyboard
748 onPowerOffClicked: {
749 shutdownFadeOutRectangle.enabled = true;
750 shutdownFadeOutRectangle.visible = true;
751 shutdownFadeOut.start();
752 }
753 }
755 Connections {
756 target: SessionBroadcast
757 onShowHome: if (shell.mode !== "greeter") showHome()
758 }
760 URLDispatcher {
761 id: urlDispatcher
762 objectName: "urlDispatcher"
763 active: shell.mode === "greeter"
764 onUrlRequested: shell.activateURL(url)
765 }
767 ItemGrabber {
768 id: itemGrabber
769 anchors.fill: parent
770 z: dialogs.z + 10
771 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
772 Connections {
773 target: stage
774 ignoreUnknownSignals: true
775 onItemSnapshotRequested: itemGrabber.capture(item)
776 }
777 }
779 Timer {
780 id: cursorHidingTimer
781 interval: 3000
782 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
783 onTriggered: cursor.opacity = 0;
784 }
786 Cursor {
787 id: cursor
788 objectName: "cursor"
789 visible: shell.hasMouse
790 z: itemGrabber.z + 1
791 topBoundaryOffset: panel.panelHeight
793 confiningItem: stage.itemConfiningMouseCursor
795 property bool mouseNeverMoved: true
796 Binding {
797 target: cursor; property: "x"; value: shell.width / 2
798 when: cursor.mouseNeverMoved && cursor.visible
799 }
800 Binding {
801 target: cursor; property: "y"; value: shell.height / 2
802 when: cursor.mouseNeverMoved && cursor.visible
803 }
805 height: units.gu(3)
807 readonly property var previewRectangle: stage.previewRectangle.target &&
808 stage.previewRectangle.target.dragging ?
809 stage.previewRectangle : null
811 onPushedLeftBoundary: {
812 if (buttons === Qt.NoButton) {
813 launcher.pushEdge(amount);
814 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
815 previewRectangle.maximizeLeft(amount);
816 }
817 }
819 onPushedRightBoundary: {
820 if (buttons === Qt.NoButton) {
821 rightEdgeBarrier.push(amount);
822 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
823 previewRectangle.maximizeRight(amount);
824 }
825 }
827 onPushedTopBoundary: {
828 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
829 previewRectangle.maximize(amount);
830 }
831 }
832 onPushedTopLeftCorner: {
833 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
834 previewRectangle.maximizeTopLeft(amount);
835 }
836 }
837 onPushedTopRightCorner: {
838 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
839 previewRectangle.maximizeTopRight(amount);
840 }
841 }
842 onPushedBottomLeftCorner: {
843 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
844 previewRectangle.maximizeBottomLeft(amount);
845 }
846 }
847 onPushedBottomRightCorner: {
848 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
849 previewRectangle.maximizeBottomRight(amount);
850 }
851 }
852 onPushStopped: {
853 if (previewRectangle) {
854 previewRectangle.stop();
855 }
856 }
858 onMouseMoved: {
859 mouseNeverMoved = false;
860 cursor.opacity = 1;
861 }
863 Behavior on opacity { UbuntuNumberAnimation {} }
864 }
866 // non-visual objects
867 KeymapSwitcher {
868 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
869 }
870 BrightnessControl {}
872 Rectangle {
873 id: shutdownFadeOutRectangle
874 z: cursor.z + 1
875 enabled: false
876 visible: false
877 color: "black"
878 anchors.fill: parent
879 opacity: 0.0
880 NumberAnimation on opacity {
881 id: shutdownFadeOut
882 from: 0.0
883 to: 1.0
884 onStopped: {
885 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
886 DBusUnitySessionService.shutdown();
887 }
888 }
889 }
890 }