on ca7d40d help sign in
unity8/qml/Shell.qml
915 lines 32 kB history raw
1/*
2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
17import QtQuick 2.4
18import QtQuick.Window 2.2
19import AccountsService 0.1
20import Unity.Application 0.1
21import Ubuntu.Components 1.3
22import Ubuntu.Components.Popups 1.3
23import Ubuntu.Gestures 0.1
24import Ubuntu.Telephony 0.1 as Telephony
25import Unity.Connectivity 0.1
26import Unity.Launcher 0.1
27import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
28import GSettings 1.0
29import ImageCache 0.1
30import Utils 0.1
31import Powerd 0.1
32import SessionBroadcast 0.1
33import "Greeter"
34import "Launcher"
35import "Panel"
36import "Components"
37import "Notifications"
38import "Stage"
39import "Tutorial"
40import "Wizard"
41import "Components/PanelState"
42import Unity.Notifications 1.0 as NotificationBackend
43import Unity.Session 0.1
44import Unity.DashCommunicator 0.1
45import Unity.Indicators 0.1 as Indicators
46import Cursor 1.1
47import WindowManager 1.0
48import Unity.Debug 0.1 as Debug
51StyledItem {
52 id: shell
54 theme.name: "Ubuntu.Components.Themes.SuruDark"
56 // to be set from outside
57 property int orientationAngle: 0
58 property int orientation
59 property Orientations orientations
60 property real nativeWidth
61 property real nativeHeight
62 property alias panelAreaShowProgress: panel.panelAreaShowProgress
63 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
64 property string mode: "full-greeter"
65 property alias oskEnabled: inputMethod.enabled
66 function updateFocusedAppOrientation() {
67 stage.updateFocusedAppOrientation();
68 }
69 function updateFocusedAppOrientationAnimated() {
70 stage.updateFocusedAppOrientationAnimated();
71 }
72 property bool hasMouse: false
73 property bool hasKeyboard: false
74 property bool hasTouchscreen: false
76 // to be read from outside
77 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
79 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
80 && stage.orientationChangesEnabled
81 && (!greeter || !greeter.animating)
83 readonly property bool showingGreeter: greeter && greeter.shown
85 property bool startingUp: true
86 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
88 property int supportedOrientations: {
89 if (startingUp) {
90 // Ensure we don't rotate during start up
91 return Qt.PrimaryOrientation;
92 } else if (showingGreeter || notifications.topmostIsFullscreen) {
93 return Qt.PrimaryOrientation;
94 } else {
95 return shell.orientations.map(stage.supportedOrientations);
96 }
97 }
99 readonly property var mainApp: stage.mainApp
101 readonly property var topLevelSurfaceList: {
102 if (!WMScreen.currentWorkspace) return null;
103 return WMScreen.currentWorkspace.windowModel
104 }
106 onMainAppChanged: {
107 if (mainApp) {
108 _onMainAppChanged(mainApp.appId);
109 }
110 }
111 Connections {
112 target: ApplicationManager
113 onFocusRequested: {
114 if (shell.mainApp && shell.mainApp.appId === appId) {
115 _onMainAppChanged(appId);
116 }
117 }
118 }
119 function _onMainAppChanged(appId) {
120 if (wizard.active && appId != "" && appId != "unity8-dash") {
121 // If this happens on first boot, we may be in the
122 // wizard while receiving a call. But a call is more
123 // important than the wizard so just bail out of it.
124 wizard.hide();
125 }
127 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
128 // If we are in the middle of a call, make dialer lockedApp and show it.
129 // This can happen if user backs out of dialer back to greeter, then
130 // launches dialer again.
131 greeter.lockedApp = appId;
132 }
133 greeter.notifyAppFocusRequested(appId);
135 panel.indicators.hide();
136 launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
137 }
139 // For autopilot consumption
140 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
142 // Note when greeter is waiting on PAM, so that we can disable edges until
143 // we know which user data to show and whether the session is locked.
144 readonly property bool waitingOnGreeter: greeter && greeter.waiting
146 property real edgeSize: units.gu(settings.edgeDragWidth)
148 WallpaperResolver {
149 id: wallpaperResolver
150 objectName: "wallpaperResolver"
152 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
153 readonly property bool hasCustomBackground: background != defaultBackground
155 // Use a cached version of the scaled-down wallpaper (as sometimes the
156 // image can be quite big compared to the device size, including for
157 // our default wallpaper). We use a name=wallpaper argument here to
158 // make sure we don't litter our cache with lots of scaled images. We
159 // only need to bother caching one at a time.
160 readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
162 GSettings {
163 id: backgroundSettings
164 schema.id: "org.gnome.desktop.background"
165 }
167 candidates: [
168 AccountsService.backgroundFile,
169 backgroundSettings.pictureUri,
170 defaultBackground
171 ]
172 }
174 readonly property alias greeter: greeterLoader.item
176 function activateApplication(appId) {
177 // Either open the app in our own session, or -- if we're acting as a
178 // greeter -- ask the user's session to open it for us.
179 if (shell.mode === "greeter") {
180 activateURL("application:///" + appId + ".desktop");
181 } else {
182 startApp(appId);
183 }
184 }
186 function activateURL(url) {
187 SessionBroadcast.requestUrlStart(AccountsService.user, url);
188 greeter.notifyUserRequestedApp();
189 panel.indicators.hide();
190 }
192 function startApp(appId) {
193 if (ApplicationManager.findApplication(appId)) {
194 ApplicationManager.requestFocusApplication(appId);
195 } else {
196 print("calling startApplication. active workspace is", WorkspaceManager.activeWorkspace)
197 ApplicationManager.startApplication(appId);
198 }
199 }
201 function startLockedApp(app) {
202 if (greeter.locked) {
203 greeter.lockedApp = app;
204 }
205 startApp(app); // locked apps are always in our same session
206 }
208 Binding {
209 target: LauncherModel
210 property: "applicationManager"
211 value: ApplicationManager
212 }
214 Component.onCompleted: {
215 finishStartUpTimer.start();
216 }
218 VolumeControl {
219 id: volumeControl
220 }
222 DashCommunicator {
223 id: dash
224 objectName: "dashCommunicator"
225 }
227 PhysicalKeysMapper {
228 id: physicalKeysMapper
229 objectName: "physicalKeysMapper"
231 onPowerKeyLongPressed: dialogs.showPowerDialog();
232 onVolumeDownTriggered: volumeControl.volumeDown();
233 onVolumeUpTriggered: volumeControl.volumeUp();
234 onScreenshotTriggered: itemGrabber.capture(shell);
235 }
237 GlobalShortcut {
238 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
239 }
241 WindowInputFilter {
242 id: inputFilter
243 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
244 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
245 }
247 WindowInputMonitor {
248 objectName: "windowInputMonitor"
249 onHomeKeyActivated: {
250 // Ignore when greeter is active, to avoid pocket presses
251 if (!greeter.active) {
252 launcher.fadeOut();
253 ApplicationManager.requestFocusApplication("unity8-dash");
254 }
255 }
256 onTouchBegun: { cursor.opacity = 0; }
257 onTouchEnded: {
258 // move the (hidden) cursor to the last known touch position
259 var mappedCoords = mapFromItem(null, pos.x, pos.y);
260 cursor.x = mappedCoords.x;
261 cursor.y = mappedCoords.y;
262 cursor.mouseNeverMoved = false;
263 }
264 }
266 GSettings {
267 id: settings
268 schema.id: "com.canonical.Unity8"
269 }
271 PanelState {
272 id: panelState
273 objectName: "panelState"
274 }
276 Item {
277 id: stages
278 objectName: "stages"
279 width: parent.width
280 height: parent.height
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: shell.topLevelSurfaceList
293 inputMethodRect: inputMethod.visibleRect
294 rightEdgePushProgress: rightEdgeBarrier.progress
296 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
297 ? "phone"
298 : shell.usageScenario
300 mode: usageScenario == "phone" ? "staged"
301 : usageScenario == "tablet" ? "stagedWithSideStage"
302 : "windowed"
304 shellOrientation: shell.orientation
305 shellOrientationAngle: shell.orientationAngle
306 orientations: shell.orientations
307 nativeWidth: shell.nativeWidth
308 nativeHeight: shell.nativeHeight
310 interactive: (!greeter || !greeter.shown)
311 && panel.indicators.fullyClosed
312 && !notifications.useModal
314 onInteractiveChanged: { if (interactive) { focus = true; } }
316 leftMargin: shell.usageScenario == "desktop" && !settings.autohideLauncher ? launcher.panelWidth: 0
317 suspended: greeter.shown
318 altTabPressed: physicalKeysMapper.altTabPressed
319 oskEnabled: shell.oskEnabled
320 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
321 panelState: panelState
323 onSpreadShownChanged: {
324 panel.indicators.hide();
325 panel.applicationMenus.hide();
326 }
327 }
329 TouchGestureArea {
330 anchors.fill: stage
332 minimumTouchPoints: 4
333 maximumTouchPoints: minimumTouchPoints
335 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
336 touchPoints.length >= minimumTouchPoints &&
337 touchPoints.length <= maximumTouchPoints
338 property bool wasPressed: false
340 onRecognisedPressChanged: {
341 if (recognisedPress) {
342 wasPressed = true;
343 }
344 }
346 onStatusChanged: {
347 if (status !== TouchGestureArea.Recognized) {
348 if (status === TouchGestureArea.WaitingForTouch) {
349 if (wasPressed && !dragging) {
350 launcher.openDrawer(true);
351 }
352 }
353 wasPressed = false;
354 }
355 }
356 }
357 }
359 InputMethod {
360 id: inputMethod
361 objectName: "inputMethod"
362 surface: shell.topLevelSurfaceList.inputMethodSurface
363 anchors {
364 fill: parent
365 topMargin: panel.panelHeight
366 leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
367 }
368 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
369 }
371 Loader {
372 id: greeterLoader
373 objectName: "greeterLoader"
374 anchors.fill: parent
375 anchors.topMargin: panel.panelHeight
376 sourceComponent: {
377 if (shell.mode != "shell") {
378 if (screenWindow.primary) return integratedGreeter;
379 return secondaryGreeter;
380 }
381 return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
382 }
383 onLoaded: {
384 item.objectName = "greeter"
385 }
386 property bool openDrawerAfterUnlock: false
387 Connections {
388 target: greeter
389 onActiveChanged: {
390 if (!greeter.active && greeterLoader.openDrawerAfterUnlock) {
391 launcher.openDrawer(false);
392 greeterLoader.openDrawerAfterUnlock = false;
393 }
394 }
395 }
396 }
398 Component {
399 id: integratedGreeter
400 Greeter {
402 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
403 hides: [launcher, panel.indicators, panel.applicationMenus]
404 tabletMode: shell.usageScenario != "phone"
405 forcedUnlock: wizard.active || shell.mode === "full-shell"
406 background: wallpaperResolver.cachedBackground
407 hasCustomBackground: wallpaperResolver.hasCustomBackground
408 allowFingerprint: !dialogs.hasActiveDialog &&
409 !notifications.topmostIsFullscreen &&
410 !panel.indicators.shown
412 // avoid overlapping with Launcher's edge drag area
413 // FIXME: Fix TouchRegistry & friends and remove this workaround
414 // Issue involves launcher's DDA getting disabled on a long
415 // left-edge drag
416 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
418 onSessionStarted: {
419 launcher.hide();
420 }
422 onTease: {
423 if (!tutorial.running) {
424 launcher.tease();
425 }
426 }
428 onEmergencyCall: startLockedApp("dialer-app")
429 }
430 }
432 Component {
433 id: secondaryGreeter
434 SecondaryGreeter {
435 hides: [launcher, panel.indicators]
436 }
437 }
439 Timer {
440 // See powerConnection for why this is useful
441 id: showGreeterDelayed
442 interval: 1
443 onTriggered: {
444 greeter.forceShow();
445 }
446 }
448 Connections {
449 id: callConnection
450 target: callManager
452 onHasCallsChanged: {
453 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
454 // We just received an incoming call while locked. The
455 // indicator will have already launched dialer-app for us, but
456 // there is a race between "hasCalls" changing and the dialer
457 // starting up. So in case we lose that race, we'll start/
458 // focus the dialer ourselves here too. Even if the indicator
459 // didn't launch the dialer for some reason (or maybe a call
460 // started via some other means), if an active call is
461 // happening, we want to be in the dialer.
462 startLockedApp("dialer-app")
463 }
464 }
465 }
467 Connections {
468 id: powerConnection
469 target: Powerd
471 onStatusChanged: {
472 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
473 !callManager.hasCalls && !wizard.active) {
474 // We don't want to simply call greeter.showNow() here, because
475 // that will take too long. Qt will delay button event
476 // handling until the greeter is done loading and may think the
477 // user held down the power button the whole time, leading to a
478 // power dialog being shown. Instead, delay showing the
479 // greeter until we've finished handling the event. We could
480 // make the greeter load asynchronously instead, but that
481 // introduces a whole host of timing issues, especially with
482 // its animations. So this is simpler.
483 showGreeterDelayed.start();
484 }
485 }
486 }
488 function showHome() {
489 greeter.notifyUserRequestedApp();
491 if (shell.mode === "greeter") {
492 SessionBroadcast.requestHomeShown(AccountsService.user);
493 } else {
494 if (!greeter.active) {
495 launcher.openDrawer(false);
496 } else {
497 greeterLoader.openDrawerAfterUnlock = true;
498 }
499 }
500 }
502 Item {
503 id: overlay
504 z: 10
506 anchors.fill: parent
508 Panel {
509 id: panel
510 objectName: "panel"
511 anchors.fill: parent //because this draws indicator menus
513 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
514 minimizedPanelHeight: units.gu(3)
515 expandedPanelHeight: units.gu(7)
516 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
517 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
519 indicators {
520 hides: [launcher]
521 available: tutorial.panelEnabled
522 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
523 && (!greeter || !greeter.hasLockedApp)
524 && !shell.waitingOnGreeter
525 && settings.enableIndicatorMenu
527 model: Indicators.IndicatorsModel {
528 // tablet and phone both use the same profile
529 // FIXME: use just "phone" for greeter too, but first fix
530 // greeter app launching to either load the app inside the
531 // greeter or tell the session to load the app. This will
532 // involve taking the url-dispatcher dbus name and using
533 // SessionBroadcast to tell the session.
534 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
535 Component.onCompleted: load();
536 }
537 }
539 applicationMenus {
540 hides: [launcher]
541 available: (!greeter || !greeter.shown)
542 && !shell.waitingOnGreeter
543 && !stage.spreadShown
544 }
546 readonly property bool focusedSurfaceIsFullscreen: shell.topLevelSurfaceList.focusedWindow
547 ? shell.topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
548 : false
549 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
550 || greeter.hasLockedApp
551 greeterShown: greeter && greeter.shown
552 hasKeyboard: shell.hasKeyboard
553 panelState: panelState
554 }
556 Launcher {
557 id: launcher
558 objectName: "launcher"
560 anchors.top: parent.top
561 anchors.topMargin: inverted ? 0 : panel.panelHeight
562 anchors.bottom: parent.bottom
563 width: parent.width
564 dragAreaWidth: shell.edgeSize
565 available: tutorial.launcherEnabled
566 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
567 && !greeter.hasLockedApp
568 && !shell.waitingOnGreeter
569 && settings.enableLauncher
570 inverted: shell.usageScenario !== "desktop"
571 superPressed: physicalKeysMapper.superPressed
572 superTabPressed: physicalKeysMapper.superTabPressed
573 panelWidth: units.gu(settings.launcherWidth)
574 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
575 blurSource: greeter.shown ? greeter : stages
576 topPanelHeight: panel.panelHeight
577 drawerEnabled: !greeter.active
579 onShowDashHome: showHome()
580 onLauncherApplicationSelected: {
581 greeter.notifyUserRequestedApp();
582 shell.activateApplication(appId);
583 }
584 onShownChanged: {
585 if (shown) {
586 panel.indicators.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: "Pointer-"+screenWindow.objectName
795 z: itemGrabber.z + 1
796 opacity: 0
797 topBoundaryOffset: panel.panelHeight
798 enabled: shell.hasMouse && screenWindow.active
799 visible: enabled
801 property bool mouseNeverMoved: true
802 Binding {
803 target: cursor; property: "x"; value: shell.width / 2
804 when: cursor.mouseNeverMoved && cursor.visible
805 }
806 Binding {
807 target: cursor; property: "y"; value: shell.height / 2
808 when: cursor.mouseNeverMoved && cursor.visible
809 }
811 confiningItem: stage.itemConfiningMouseCursor
813 height: units.gu(3)
815 readonly property var previewRectangle: stage.previewRectangle.target &&
816 stage.previewRectangle.target.dragging ?
817 stage.previewRectangle : null
819 onPushedLeftBoundary: {
820 if (buttons === Qt.NoButton) {
821 launcher.pushEdge(amount);
822 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
823 previewRectangle.maximizeLeft(amount);
824 }
825 }
827 onPushedRightBoundary: {
828 if (buttons === Qt.NoButton) {
829 rightEdgeBarrier.push(amount);
830 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
831 previewRectangle.maximizeRight(amount);
832 }
833 }
835 onPushedTopBoundary: {
836 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
837 previewRectangle.maximize(amount);
838 }
839 }
840 onPushedTopLeftCorner: {
841 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
842 previewRectangle.maximizeTopLeft(amount);
843 }
844 }
845 onPushedTopRightCorner: {
846 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
847 previewRectangle.maximizeTopRight(amount);
848 }
849 }
850 onPushedBottomLeftCorner: {
851 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
852 previewRectangle.maximizeBottomLeft(amount);
853 }
854 }
855 onPushedBottomRightCorner: {
856 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
857 previewRectangle.maximizeBottomRight(amount);
858 }
859 }
860 onPushStopped: {
861 if (previewRectangle) {
862 previewRectangle.stop();
863 }
864 }
866 onMouseMoved: {
867 mouseNeverMoved = false;
868 cursor.opacity = 1;
869 }
871 Behavior on opacity { UbuntuNumberAnimation {} }
872 }
874 // non-visual objects
875 KeymapSwitcher {
876 focusedSurface: shell.topLevelSurfaceList.focusedWindow ? shell.topLevelSurfaceList.focusedWindow.surface : null
877 }
878 BrightnessControl {}
880 Rectangle {
881 id: shutdownFadeOutRectangle
882 z: cursor.z + 1
883 enabled: false
884 visible: false
885 color: "black"
886 anchors.fill: parent
887 opacity: 0.0
888 NumberAnimation on opacity {
889 id: shutdownFadeOut
890 from: 0.0
891 to: 1.0
892 onStopped: {
893 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
894 DBusUnitySessionService.shutdown();
895 }
896 }
897 }
898 }
900 Loader {
901 z: shutdownFadeOutRectangle.z + 1
902 active: DebuggingController.logOverlay
904 sourceComponent: Text {
905 width: shell.width
906 height: shell.height
908 text: consoleLog.out
909 Debug.ConsoleLog {
910 id: consoleLog
911 enabled: true
912 }
913 }
914 }