on 944783e help sign in
unity8/qml/Shell.qml
880 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 greeter.forceShow();
430 }
431 }
433 Connections {
434 id: callConnection
435 target: callManager
437 onHasCallsChanged: {
438 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
439 // We just received an incoming call while locked. The
440 // indicator will have already launched dialer-app for us, but
441 // there is a race between "hasCalls" changing and the dialer
442 // starting up. So in case we lose that race, we'll start/
443 // focus the dialer ourselves here too. Even if the indicator
444 // didn't launch the dialer for some reason (or maybe a call
445 // started via some other means), if an active call is
446 // happening, we want to be in the dialer.
447 startLockedApp("dialer-app")
448 }
449 }
450 }
452 Connections {
453 id: powerConnection
454 target: Powerd
456 onStatusChanged: {
457 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
458 !callManager.hasCalls && !wizard.active) {
459 // We don't want to simply call greeter.showNow() here, because
460 // that will take too long. Qt will delay button event
461 // handling until the greeter is done loading and may think the
462 // user held down the power button the whole time, leading to a
463 // power dialog being shown. Instead, delay showing the
464 // greeter until we've finished handling the event. We could
465 // make the greeter load asynchronously instead, but that
466 // introduces a whole host of timing issues, especially with
467 // its animations. So this is simpler.
468 showGreeterDelayed.start();
469 }
470 }
471 }
473 function showHome() {
474 greeter.notifyUserRequestedApp();
476 if (shell.mode === "greeter") {
477 SessionBroadcast.requestHomeShown(AccountsService.user);
478 } else {
479 if (!greeter.active) {
480 launcher.openDrawer(false);
481 } else {
482 greeterLoader.openDrawerAfterUnlock = true;
483 }
484 }
485 }
487 Item {
488 id: overlay
489 z: 10
491 anchors.fill: parent
493 Panel {
494 id: panel
495 objectName: "panel"
496 anchors.fill: parent //because this draws indicator menus
498 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
499 minimizedPanelHeight: units.gu(3)
500 expandedPanelHeight: units.gu(7)
501 indicatorMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
502 applicationMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
504 indicators {
505 hides: [launcher]
506 available: tutorial.panelEnabled
507 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
508 && (!greeter || !greeter.hasLockedApp)
509 && !shell.waitingOnGreeter
510 && settings.enableIndicatorMenu
512 model: Indicators.IndicatorsModel {
513 // tablet and phone both use the same profile
514 // FIXME: use just "phone" for greeter too, but first fix
515 // greeter app launching to either load the app inside the
516 // greeter or tell the session to load the app. This will
517 // involve taking the url-dispatcher dbus name and using
518 // SessionBroadcast to tell the session.
519 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
520 Component.onCompleted: load();
521 }
522 }
524 applicationMenus {
525 hides: [launcher]
526 available: (!greeter || !greeter.shown)
527 && !shell.waitingOnGreeter
528 && !stage.spreadShown
529 }
531 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
532 ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
533 : false
534 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
535 || greeter.hasLockedApp
536 greeterShown: greeter && greeter.shown
537 hasKeyboard: shell.hasKeyboard
538 }
540 Launcher {
541 id: launcher
542 objectName: "launcher"
544 anchors.top: parent.top
545 anchors.topMargin: inverted ? 0 : panel.panelHeight
546 anchors.bottom: parent.bottom
547 width: parent.width
548 dragAreaWidth: shell.edgeSize
549 available: tutorial.launcherEnabled
550 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
551 && !greeter.hasLockedApp
552 && !shell.waitingOnGreeter
553 && settings.enableLauncher
554 inverted: shell.usageScenario !== "desktop"
555 superPressed: physicalKeysMapper.superPressed
556 superTabPressed: physicalKeysMapper.superTabPressed
557 panelWidth: units.gu(settings.launcherWidth)
558 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
559 blurSource: greeter.shown ? greeter : stages
560 topPanelHeight: panel.panelHeight
561 drawerEnabled: !greeter.active
563 onShowDashHome: showHome()
564 onLauncherApplicationSelected: {
565 greeter.notifyUserRequestedApp();
566 shell.activateApplication(appId);
567 }
568 onShownChanged: {
569 if (shown) {
570 panel.indicators.hide()
571 }
572 }
573 onFocusChanged: {
574 if (!focus) {
575 stage.focus = true;
576 }
577 }
579 GlobalShortcut {
580 shortcut: Qt.MetaModifier | Qt.Key_A
581 onTriggered: {
582 launcher.openDrawer(true);
583 }
584 }
585 GlobalShortcut {
586 shortcut: Qt.AltModifier | Qt.Key_F1
587 onTriggered: {
588 launcher.openForKeyboardNavigation();
589 }
590 }
591 GlobalShortcut {
592 shortcut: Qt.MetaModifier | Qt.Key_0
593 onTriggered: {
594 if (LauncherModel.get(9)) {
595 activateApplication(LauncherModel.get(9).appId);
596 }
597 }
598 }
599 Repeater {
600 model: 9
601 GlobalShortcut {
602 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
603 onTriggered: {
604 if (LauncherModel.get(index)) {
605 activateApplication(LauncherModel.get(index).appId);
606 }
607 }
608 }
609 }
610 }
612 KeyboardShortcutsOverlay {
613 objectName: "shortcutsOverlay"
614 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
615 && height < parent.height - padding - panel.panelHeight
616 anchors.centerIn: parent
617 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
618 anchors.verticalCenterOffset: panel.panelHeight/2
619 visible: opacity > 0
620 opacity: enabled ? 0.95 : 0
622 Behavior on opacity {
623 UbuntuNumberAnimation {}
624 }
625 }
627 Tutorial {
628 id: tutorial
629 objectName: "tutorial"
630 anchors.fill: parent
632 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
633 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
634 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
635 inputMethod.visible ||
636 (launcher.shown && !launcher.lockedVisible) ||
637 panel.indicators.shown || stage.rightEdgeDragProgress > 0
638 usageScenario: shell.usageScenario
639 lastInputTimestamp: inputFilter.lastInputTimestamp
640 launcher: launcher
641 panel: panel
642 stage: stage
643 }
645 Wizard {
646 id: wizard
647 objectName: "wizard"
648 anchors.fill: parent
649 deferred: shell.mode === "greeter"
651 function unlockWhenDoneWithWizard() {
652 if (!active) {
653 Connectivity.unlockAllModems();
654 }
655 }
657 Component.onCompleted: unlockWhenDoneWithWizard()
658 onActiveChanged: unlockWhenDoneWithWizard()
659 }
661 MouseArea { // modal notifications prevent interacting with other contents
662 anchors.fill: parent
663 visible: notifications.useModal
664 enabled: visible
665 }
667 Notifications {
668 id: notifications
670 model: NotificationBackend.Model
671 margin: units.gu(1)
672 hasMouse: shell.hasMouse
673 background: wallpaperResolver.cachedBackground
675 y: topmostIsFullscreen ? 0 : panel.panelHeight
676 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
678 states: [
679 State {
680 name: "narrow"
681 when: overlay.width <= units.gu(60)
682 AnchorChanges {
683 target: notifications
684 anchors.left: parent.left
685 anchors.right: parent.right
686 }
687 },
688 State {
689 name: "wide"
690 when: overlay.width > units.gu(60)
691 AnchorChanges {
692 target: notifications
693 anchors.left: undefined
694 anchors.right: parent.right
695 }
696 PropertyChanges { target: notifications; width: units.gu(38) }
697 }
698 ]
699 }
701 EdgeBarrier {
702 id: rightEdgeBarrier
703 enabled: !greeter.shown
705 // NB: it does its own positioning according to the specified edge
706 edge: Qt.RightEdge
708 onPassed: {
709 panel.indicators.hide()
710 }
712 material: Component {
713 Item {
714 Rectangle {
715 width: parent.height
716 height: parent.width
717 rotation: 90
718 anchors.centerIn: parent
719 gradient: Gradient {
720 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
721 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
722 }
723 }
724 }
725 }
726 }
727 }
729 Dialogs {
730 id: dialogs
731 objectName: "dialogs"
732 anchors.fill: parent
733 visible: hasActiveDialog
734 z: overlay.z + 10
735 usageScenario: shell.usageScenario
736 hasKeyboard: shell.hasKeyboard
737 onPowerOffClicked: {
738 shutdownFadeOutRectangle.enabled = true;
739 shutdownFadeOutRectangle.visible = true;
740 shutdownFadeOut.start();
741 }
742 }
744 Connections {
745 target: SessionBroadcast
746 onShowHome: if (shell.mode !== "greeter") showHome()
747 }
749 URLDispatcher {
750 id: urlDispatcher
751 objectName: "urlDispatcher"
752 active: shell.mode === "greeter"
753 onUrlRequested: shell.activateURL(url)
754 }
756 ItemGrabber {
757 id: itemGrabber
758 anchors.fill: parent
759 z: dialogs.z + 10
760 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
761 Connections {
762 target: stage
763 ignoreUnknownSignals: true
764 onItemSnapshotRequested: itemGrabber.capture(item)
765 }
766 }
768 Timer {
769 id: cursorHidingTimer
770 interval: 3000
771 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
772 onTriggered: cursor.opacity = 0;
773 }
775 Cursor {
776 id: cursor
777 objectName: "cursor"
778 visible: shell.hasMouse
779 z: itemGrabber.z + 1
780 topBoundaryOffset: panel.panelHeight
782 confiningItem: stage.itemConfiningMouseCursor
784 property bool mouseNeverMoved: true
785 Binding {
786 target: cursor; property: "x"; value: shell.width / 2
787 when: cursor.mouseNeverMoved && cursor.visible
788 }
789 Binding {
790 target: cursor; property: "y"; value: shell.height / 2
791 when: cursor.mouseNeverMoved && cursor.visible
792 }
794 height: units.gu(3)
796 readonly property var previewRectangle: stage.previewRectangle.target &&
797 stage.previewRectangle.target.dragging ?
798 stage.previewRectangle : null
800 onPushedLeftBoundary: {
801 if (buttons === Qt.NoButton) {
802 launcher.pushEdge(amount);
803 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
804 previewRectangle.maximizeLeft(amount);
805 }
806 }
808 onPushedRightBoundary: {
809 if (buttons === Qt.NoButton) {
810 rightEdgeBarrier.push(amount);
811 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
812 previewRectangle.maximizeRight(amount);
813 }
814 }
816 onPushedTopBoundary: {
817 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
818 previewRectangle.maximize(amount);
819 }
820 }
821 onPushedTopLeftCorner: {
822 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
823 previewRectangle.maximizeTopLeft(amount);
824 }
825 }
826 onPushedTopRightCorner: {
827 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
828 previewRectangle.maximizeTopRight(amount);
829 }
830 }
831 onPushedBottomLeftCorner: {
832 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
833 previewRectangle.maximizeBottomLeft(amount);
834 }
835 }
836 onPushedBottomRightCorner: {
837 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
838 previewRectangle.maximizeBottomRight(amount);
839 }
840 }
841 onPushStopped: {
842 if (previewRectangle) {
843 previewRectangle.stop();
844 }
845 }
847 onMouseMoved: {
848 mouseNeverMoved = false;
849 cursor.opacity = 1;
850 }
852 Behavior on opacity { UbuntuNumberAnimation {} }
853 }
855 // non-visual objects
856 KeymapSwitcher {
857 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
858 }
859 BrightnessControl {}
861 Rectangle {
862 id: shutdownFadeOutRectangle
863 z: cursor.z + 1
864 enabled: false
865 visible: false
866 color: "black"
867 anchors.fill: parent
868 opacity: 0.0
869 NumberAnimation on opacity {
870 id: shutdownFadeOut
871 from: 0.0
872 to: 1.0
873 onStopped: {
874 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
875 DBusUnitySessionService.shutdown();
876 }
877 }
878 }
879 }