on d52dc2c help sign in
unity8/qml/Shell.qml
918 lines 33 kB history raw
1/*
2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 * Copyright (C) 2019-2020 UBports Foundation
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
18import QtQuick 2.4
19import QtQuick.Window 2.2
20import AccountsService 0.1
21import Unity.Application 0.1
22import Ubuntu.Components 1.3
23import Ubuntu.Components.Popups 1.3
24import Ubuntu.Gestures 0.1
25import Ubuntu.Telephony 0.1 as Telephony
26import Unity.Connectivity 0.1
27import Unity.Launcher 0.1
28import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
29import GSettings 1.0
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.Indicators 0.1 as Indicators
44import Cursor 1.1
45import WindowManager 1.0
48StyledItem {
49 id: shell
51 theme.name: "Ubuntu.Components.Themes.SuruDark"
53 // to be set from outside
54 property int orientationAngle: 0
55 property int orientation
56 property Orientations orientations
57 property real nativeWidth
58 property real nativeHeight
59 property alias panelAreaShowProgress: panel.panelAreaShowProgress
60 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
61 property string mode: "full-greeter"
62 property alias oskEnabled: inputMethod.enabled
63 function updateFocusedAppOrientation() {
64 stage.updateFocusedAppOrientation();
65 }
66 function updateFocusedAppOrientationAnimated() {
67 stage.updateFocusedAppOrientationAnimated();
68 }
69 property bool hasMouse: false
70 property bool hasKeyboard: false
71 property bool hasTouchscreen: false
72 property bool supportsMultiColorLed: true
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 _onMainAppChanged((mainApp ? mainApp.appId : ""));
101 }
102 Connections {
103 target: ApplicationManager
104 onFocusRequested: {
105 if (shell.mainApp && shell.mainApp.appId === appId) {
106 _onMainAppChanged(appId);
107 }
108 }
109 }
111 // Calls attention back to the most important thing that's been focused
112 // (ex: phone calls go over Wizard, app focuses go over indicators, greeter
113 // goes over everything if it is locked)
114 // Must be called whenever app focus changes occur, even if the focus change
115 // is "nothing is focused". In that case, call with appId = ""
116 function _onMainAppChanged(appId) {
118 if (appId !== "") {
119 if (wizard.active) {
120 // If this happens on first boot, we may be in the
121 // wizard while receiving a call. A call is more
122 // important than the wizard so just bail out of it.
123 wizard.hide();
124 }
126 if (appId === "dialer-app" && callManager.hasCalls && greeter.locked) {
127 // If we are in the middle of a call, make dialer lockedApp. The
128 // Greeter will show it when it's notified of the focus.
129 // This can happen if user backs out of dialer back to greeter, then
130 // launches dialer again.
131 greeter.lockedApp = appId;
132 }
134 panel.indicators.hide();
135 launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
136 }
138 // *Always* make sure the greeter knows that the focused app changed
139 if (greeter) greeter.notifyAppFocusRequested(appId);
140 }
142 // For autopilot consumption
143 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
145 // Note when greeter is waiting on PAM, so that we can disable edges until
146 // we know which user data to show and whether the session is locked.
147 readonly property bool waitingOnGreeter: greeter && greeter.waiting
149 // True when the user is logged in with no apps running
150 readonly property bool atDesktop: topLevelSurfaceList && greeter && topLevelSurfaceList.count === 0 && !greeter.active
152 onAtDesktopChanged: {
153 if (atDesktop && stage) {
154 stage.closeSpread();
155 }
156 }
158 property real edgeSize: units.gu(settings.edgeDragWidth)
160 WallpaperResolver {
161 id: wallpaperResolver
162 objectName: "wallpaperResolver"
164 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
165 readonly property bool hasCustomBackground: background != defaultBackground
167 GSettings {
168 id: backgroundSettings
169 schema.id: "org.gnome.desktop.background"
170 }
172 candidates: [
173 AccountsService.backgroundFile,
174 backgroundSettings.pictureUri,
175 defaultBackground
176 ]
177 }
179 readonly property alias greeter: greeterLoader.item
181 function activateApplication(appId) {
182 topLevelSurfaceList.pendingActivation();
184 // Either open the app in our own session, or -- if we're acting as a
185 // greeter -- ask the user's session to open it for us.
186 if (shell.mode === "greeter") {
187 activateURL("application:///" + appId + ".desktop");
188 } else {
189 startApp(appId);
190 }
191 stage.focus = true;
192 }
194 function activateURL(url) {
195 SessionBroadcast.requestUrlStart(AccountsService.user, url);
196 greeter.notifyUserRequestedApp();
197 panel.indicators.hide();
198 }
200 function startApp(appId) {
201 if (ApplicationManager.findApplication(appId)) {
202 ApplicationManager.requestFocusApplication(appId);
203 } else {
204 ApplicationManager.startApplication(appId);
205 }
206 }
208 function startLockedApp(app) {
209 topLevelSurfaceList.pendingActivation();
211 if (greeter.locked) {
212 greeter.lockedApp = app;
213 }
214 startApp(app); // locked apps are always in our same session
215 }
217 Binding {
218 target: LauncherModel
219 property: "applicationManager"
220 value: ApplicationManager
221 }
223 Component.onCompleted: {
224 finishStartUpTimer.start();
225 }
227 VolumeControl {
228 id: volumeControl
229 }
231 PhysicalKeysMapper {
232 id: physicalKeysMapper
233 objectName: "physicalKeysMapper"
235 onPowerKeyLongPressed: dialogs.showPowerDialog();
236 onVolumeDownTriggered: volumeControl.volumeDown();
237 onVolumeUpTriggered: volumeControl.volumeUp();
238 onScreenshotTriggered: itemGrabber.capture(shell);
239 }
241 GlobalShortcut {
242 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
243 }
245 WindowInputFilter {
246 id: inputFilter
247 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
248 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
249 }
251 WindowInputMonitor {
252 objectName: "windowInputMonitor"
253 onHomeKeyActivated: {
254 // Ignore when greeter is active, to avoid pocket presses
255 if (!greeter.active) {
256 launcher.toggleDrawer(false);
257 }
258 }
259 onTouchBegun: { cursor.opacity = 0; }
260 onTouchEnded: {
261 // move the (hidden) cursor to the last known touch position
262 var mappedCoords = mapFromItem(null, pos.x, pos.y);
263 cursor.x = mappedCoords.x;
264 cursor.y = mappedCoords.y;
265 cursor.mouseNeverMoved = false;
266 }
267 }
269 AvailableDesktopArea {
270 id: availableDesktopAreaItem
271 anchors.fill: parent
272 anchors.topMargin: panel.fullscreenMode ? 0 : panel.minimizedPanelHeight
273 anchors.leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
274 }
276 GSettings {
277 id: settings
278 schema.id: "com.canonical.Unity8"
279 }
281 Item {
282 id: stages
283 objectName: "stages"
284 width: parent.width
285 height: parent.height
287 SurfaceManager {
288 id: surfaceMan
289 objectName: "surfaceManager"
290 }
291 TopLevelWindowModel {
292 id: topLevelSurfaceList
293 objectName: "topLevelSurfaceList"
294 applicationManager: ApplicationManager // it's a singleton
295 surfaceManager: surfaceMan
296 }
298 Stage {
299 id: stage
300 objectName: "stage"
301 anchors.fill: parent
302 focus: true
304 dragAreaWidth: shell.edgeSize
305 background: wallpaperResolver.background
307 applicationManager: ApplicationManager
308 topLevelSurfaceList: topLevelSurfaceList
309 inputMethodRect: inputMethod.visibleRect
310 rightEdgePushProgress: rightEdgeBarrier.progress
311 availableDesktopArea: availableDesktopAreaItem
313 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
314 ? "phone"
315 : shell.usageScenario
317 mode: usageScenario == "phone" ? "staged"
318 : usageScenario == "tablet" ? "stagedWithSideStage"
319 : "windowed"
321 shellOrientation: shell.orientation
322 shellOrientationAngle: shell.orientationAngle
323 orientations: shell.orientations
324 nativeWidth: shell.nativeWidth
325 nativeHeight: shell.nativeHeight
327 allowInteractivity: (!greeter || !greeter.shown)
328 && panel.indicators.fullyClosed
329 && !notifications.useModal
330 && !launcher.takesFocus
332 suspended: greeter.shown
333 altTabPressed: physicalKeysMapper.altTabPressed
334 oskEnabled: shell.oskEnabled
335 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
337 onSpreadShownChanged: {
338 panel.indicators.hide();
339 panel.applicationMenus.hide();
340 }
341 }
343 TouchGestureArea {
344 anchors.fill: stage
346 minimumTouchPoints: 4
347 maximumTouchPoints: minimumTouchPoints
349 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
350 touchPoints.length >= minimumTouchPoints &&
351 touchPoints.length <= maximumTouchPoints
352 property bool wasPressed: false
354 onRecognisedPressChanged: {
355 if (recognisedPress) {
356 wasPressed = true;
357 }
358 }
360 onStatusChanged: {
361 if (status !== TouchGestureArea.Recognized) {
362 if (status === TouchGestureArea.WaitingForTouch) {
363 if (wasPressed && !dragging) {
364 launcher.toggleDrawer(true);
365 }
366 }
367 wasPressed = false;
368 }
369 }
370 }
371 }
373 InputMethod {
374 id: inputMethod
375 objectName: "inputMethod"
376 anchors {
377 fill: parent
378 topMargin: panel.panelHeight
379 leftMargin: (launcher.lockedByUser && launcher.lockAllowed) ? launcher.panelWidth : 0
380 }
381 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
382 }
384 Loader {
385 id: greeterLoader
386 objectName: "greeterLoader"
387 anchors.fill: parent
388 anchors.topMargin: panel.panelHeight
389 sourceComponent: shell.mode != "shell" ? integratedGreeter :
390 Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
391 onLoaded: {
392 item.objectName = "greeter"
393 }
394 property bool toggleDrawerAfterUnlock: false
395 Connections {
396 target: greeter
397 onActiveChanged: {
398 if (greeter.active)
399 return
401 // Show drawer in case showHome() requests it
402 if (greeterLoader.toggleDrawerAfterUnlock) {
403 launcher.toggleDrawer(false);
404 greeterLoader.toggleDrawerAfterUnlock = false;
405 } else {
406 launcher.hide();
407 }
408 }
409 }
410 }
412 Component {
413 id: integratedGreeter
414 Greeter {
416 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
417 hides: [launcher, panel.indicators, panel.applicationMenus]
418 tabletMode: shell.usageScenario != "phone"
419 forcedUnlock: wizard.active || shell.mode === "full-shell"
420 background: wallpaperResolver.background
421 hasCustomBackground: wallpaperResolver.hasCustomBackground
422 allowFingerprint: !dialogs.hasActiveDialog &&
423 !notifications.topmostIsFullscreen &&
424 !panel.indicators.shown
426 // avoid overlapping with Launcher's edge drag area
427 // FIXME: Fix TouchRegistry & friends and remove this workaround
428 // Issue involves launcher's DDA getting disabled on a long
429 // left-edge drag
430 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
432 onTease: {
433 if (!tutorial.running) {
434 launcher.tease();
435 }
436 }
438 onEmergencyCall: startLockedApp("dialer-app")
439 }
440 }
442 Timer {
443 // See powerConnection for why this is useful
444 id: showGreeterDelayed
445 interval: 1
446 onTriggered: {
447 // Go through the dbus service, because it has checks for whether
448 // we are even allowed to lock or not.
449 DBusUnitySessionService.PromptLock();
450 }
451 }
453 Connections {
454 id: callConnection
455 target: callManager
457 onHasCallsChanged: {
458 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "dialer-app") {
459 // We just received an incoming call while locked. The
460 // indicator will have already launched dialer-app for us, but
461 // there is a race between "hasCalls" changing and the dialer
462 // starting up. So in case we lose that race, we'll start/
463 // focus the dialer ourselves here too. Even if the indicator
464 // didn't launch the dialer for some reason (or maybe a call
465 // started via some other means), if an active call is
466 // happening, we want to be in the dialer.
467 startLockedApp("dialer-app")
468 }
469 }
470 }
472 Connections {
473 id: powerConnection
474 target: Powerd
476 onStatusChanged: {
477 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
478 !callManager.hasCalls && !wizard.active) {
479 // We don't want to simply call greeter.showNow() here, because
480 // that will take too long. Qt will delay button event
481 // handling until the greeter is done loading and may think the
482 // user held down the power button the whole time, leading to a
483 // power dialog being shown. Instead, delay showing the
484 // greeter until we've finished handling the event. We could
485 // make the greeter load asynchronously instead, but that
486 // introduces a whole host of timing issues, especially with
487 // its animations. So this is simpler.
488 showGreeterDelayed.start();
489 }
490 }
491 }
493 function showHome() {
494 greeter.notifyUserRequestedApp();
496 if (shell.mode === "greeter") {
497 SessionBroadcast.requestHomeShown(AccountsService.user);
498 } else {
499 if (!greeter.active) {
500 launcher.toggleDrawer(false);
501 } else {
502 greeterLoader.toggleDrawerAfterUnlock = true;
503 }
504 }
505 }
507 Item {
508 id: overlay
509 z: 10
511 anchors.fill: parent
513 Panel {
514 id: panel
515 objectName: "panel"
516 anchors.fill: parent //because this draws indicator menus
518 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
519 minimizedPanelHeight: units.gu(3)
520 expandedPanelHeight: units.gu(7)
521 applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
523 indicators {
524 hides: [launcher]
525 available: tutorial.panelEnabled
526 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
527 && (!greeter || !greeter.hasLockedApp)
528 && !shell.waitingOnGreeter
529 && settings.enableIndicatorMenu
531 model: Indicators.IndicatorsModel {
532 // tablet and phone both use the same profile
533 // FIXME: use just "phone" for greeter too, but first fix
534 // greeter app launching to either load the app inside the
535 // greeter or tell the session to load the app. This will
536 // involve taking the url-dispatcher dbus name and using
537 // SessionBroadcast to tell the session.
538 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
539 Component.onCompleted: load();
540 }
541 }
543 applicationMenus {
544 hides: [launcher]
545 available: (!greeter || !greeter.shown)
546 && !shell.waitingOnGreeter
547 && !stage.spreadShown
548 }
550 readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
551 ? topLevelSurfaceList.focusedWindow.state == Mir.FullscreenState
552 : false
553 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
554 || greeter.hasLockedApp
555 greeterShown: greeter && greeter.shown
556 hasKeyboard: shell.hasKeyboard
557 supportsMultiColorLed: shell.supportsMultiColorLed
558 }
560 Launcher {
561 id: launcher
562 objectName: "launcher"
564 anchors.top: parent.top
565 anchors.topMargin: inverted ? 0 : panel.panelHeight
566 anchors.bottom: parent.bottom
567 width: parent.width
568 dragAreaWidth: shell.edgeSize
569 available: tutorial.launcherEnabled
570 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
571 && !greeter.hasLockedApp
572 && !shell.waitingOnGreeter
573 inverted: shell.usageScenario !== "desktop"
574 superPressed: physicalKeysMapper.superPressed
575 superTabPressed: physicalKeysMapper.superTabPressed
576 panelWidth: units.gu(settings.launcherWidth)
577 lockedVisible: (lockedByUser || shell.atDesktop) && lockAllowed
578 topPanelHeight: panel.panelHeight
579 drawerEnabled: !greeter.active && tutorial.launcherLongSwipeEnabled
580 privateMode: greeter.active
581 background: wallpaperResolver.background
583 // It can be assumed that the Launcher and Panel would overlap if
584 // the Panel is open and taking up the full width of the shell
585 readonly property bool collidingWithPanel: panel && (!panel.fullyClosed && !panel.partialWidth)
587 // The "autohideLauncher" setting is only valid in desktop mode
588 readonly property bool lockedByUser: (shell.usageScenario == "desktop" && !settings.autohideLauncher)
590 // The Launcher should absolutely not be locked visible under some
591 // conditions
592 readonly property bool lockAllowed: !collidingWithPanel && !panel.fullscreenMode && !wizard.active && !tutorial.demonstrateLauncher
594 onShowDashHome: showHome()
595 onLauncherApplicationSelected: {
596 greeter.notifyUserRequestedApp();
597 shell.activateApplication(appId);
598 }
599 onShownChanged: {
600 if (shown) {
601 panel.indicators.hide();
602 panel.applicationMenus.hide();
603 }
604 }
605 onDrawerShownChanged: {
606 if (drawerShown) {
607 panel.indicators.hide();
608 panel.applicationMenus.hide();
609 }
610 }
611 onFocusChanged: {
612 if (!focus) {
613 stage.focus = true;
614 }
615 }
617 GlobalShortcut {
618 shortcut: Qt.MetaModifier | Qt.Key_A
619 onTriggered: {
620 launcher.toggleDrawer(true);
621 }
622 }
623 GlobalShortcut {
624 shortcut: Qt.AltModifier | Qt.Key_F1
625 onTriggered: {
626 launcher.openForKeyboardNavigation();
627 }
628 }
629 GlobalShortcut {
630 shortcut: Qt.MetaModifier | Qt.Key_0
631 onTriggered: {
632 if (LauncherModel.get(9)) {
633 activateApplication(LauncherModel.get(9).appId);
634 }
635 }
636 }
637 Repeater {
638 model: 9
639 GlobalShortcut {
640 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
641 onTriggered: {
642 if (LauncherModel.get(index)) {
643 activateApplication(LauncherModel.get(index).appId);
644 }
645 }
646 }
647 }
648 }
650 KeyboardShortcutsOverlay {
651 objectName: "shortcutsOverlay"
652 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
653 && height < parent.height - padding - panel.panelHeight
654 anchors.centerIn: parent
655 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
656 anchors.verticalCenterOffset: panel.panelHeight/2
657 visible: opacity > 0
658 opacity: enabled ? 0.95 : 0
660 Behavior on opacity {
661 UbuntuNumberAnimation {}
662 }
663 }
665 Tutorial {
666 id: tutorial
667 objectName: "tutorial"
668 anchors.fill: parent
670 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
671 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
672 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
673 inputMethod.visible ||
674 (launcher.shown && !launcher.lockedVisible) ||
675 panel.indicators.shown || stage.rightEdgeDragProgress > 0
676 usageScenario: shell.usageScenario
677 lastInputTimestamp: inputFilter.lastInputTimestamp
678 launcher: launcher
679 panel: panel
680 stage: stage
681 }
683 Wizard {
684 id: wizard
685 objectName: "wizard"
686 anchors.fill: parent
687 deferred: shell.mode === "greeter"
689 function unlockWhenDoneWithWizard() {
690 if (!active) {
691 Connectivity.unlockAllModems();
692 }
693 }
695 Component.onCompleted: unlockWhenDoneWithWizard()
696 onActiveChanged: unlockWhenDoneWithWizard()
697 }
699 MouseArea { // modal notifications prevent interacting with other contents
700 anchors.fill: parent
701 visible: notifications.useModal
702 enabled: visible
703 }
705 Notifications {
706 id: notifications
708 model: NotificationBackend.Model
709 margin: units.gu(1)
710 hasMouse: shell.hasMouse
711 background: wallpaperResolver.background
713 y: topmostIsFullscreen ? 0 : panel.panelHeight
714 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
716 states: [
717 State {
718 name: "narrow"
719 when: overlay.width <= units.gu(60)
720 AnchorChanges {
721 target: notifications
722 anchors.left: parent.left
723 anchors.right: parent.right
724 }
725 },
726 State {
727 name: "wide"
728 when: overlay.width > units.gu(60)
729 AnchorChanges {
730 target: notifications
731 anchors.left: undefined
732 anchors.right: parent.right
733 }
734 PropertyChanges { target: notifications; width: units.gu(38) }
735 }
736 ]
737 }
739 EdgeBarrier {
740 id: rightEdgeBarrier
741 enabled: !greeter.shown
743 // NB: it does its own positioning according to the specified edge
744 edge: Qt.RightEdge
746 onPassed: {
747 panel.indicators.hide()
748 }
750 material: Component {
751 Item {
752 Rectangle {
753 width: parent.height
754 height: parent.width
755 rotation: 90
756 anchors.centerIn: parent
757 gradient: Gradient {
758 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
759 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
760 }
761 }
762 }
763 }
764 }
765 }
767 Dialogs {
768 id: dialogs
769 objectName: "dialogs"
770 anchors.fill: parent
771 visible: hasActiveDialog
772 z: overlay.z + 10
773 usageScenario: shell.usageScenario
774 hasKeyboard: shell.hasKeyboard
775 onPowerOffClicked: {
776 shutdownFadeOutRectangle.enabled = true;
777 shutdownFadeOutRectangle.visible = true;
778 shutdownFadeOut.start();
779 }
780 }
782 Connections {
783 target: SessionBroadcast
784 onShowHome: if (shell.mode !== "greeter") showHome()
785 }
787 URLDispatcher {
788 id: urlDispatcher
789 objectName: "urlDispatcher"
790 active: shell.mode === "greeter"
791 onUrlRequested: shell.activateURL(url)
792 }
794 ItemGrabber {
795 id: itemGrabber
796 anchors.fill: parent
797 z: dialogs.z + 10
798 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
799 Connections {
800 target: stage
801 ignoreUnknownSignals: true
802 onItemSnapshotRequested: itemGrabber.capture(item)
803 }
804 }
806 Timer {
807 id: cursorHidingTimer
808 interval: 3000
809 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
810 onTriggered: cursor.opacity = 0;
811 }
813 Cursor {
814 id: cursor
815 objectName: "cursor"
816 visible: shell.hasMouse
817 z: itemGrabber.z + 1
818 topBoundaryOffset: panel.panelHeight
820 confiningItem: stage.itemConfiningMouseCursor
822 property bool mouseNeverMoved: true
823 Binding {
824 target: cursor; property: "x"; value: shell.width / 2
825 when: cursor.mouseNeverMoved && cursor.visible
826 }
827 Binding {
828 target: cursor; property: "y"; value: shell.height / 2
829 when: cursor.mouseNeverMoved && cursor.visible
830 }
832 height: units.gu(3)
834 readonly property var previewRectangle: stage.previewRectangle.target &&
835 stage.previewRectangle.target.dragging ?
836 stage.previewRectangle : null
838 onPushedLeftBoundary: {
839 if (buttons === Qt.NoButton) {
840 launcher.pushEdge(amount);
841 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
842 previewRectangle.maximizeLeft(amount);
843 }
844 }
846 onPushedRightBoundary: {
847 if (buttons === Qt.NoButton) {
848 rightEdgeBarrier.push(amount);
849 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
850 previewRectangle.maximizeRight(amount);
851 }
852 }
854 onPushedTopBoundary: {
855 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
856 previewRectangle.maximize(amount);
857 }
858 }
859 onPushedTopLeftCorner: {
860 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
861 previewRectangle.maximizeTopLeft(amount);
862 }
863 }
864 onPushedTopRightCorner: {
865 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
866 previewRectangle.maximizeTopRight(amount);
867 }
868 }
869 onPushedBottomLeftCorner: {
870 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
871 previewRectangle.maximizeBottomLeft(amount);
872 }
873 }
874 onPushedBottomRightCorner: {
875 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
876 previewRectangle.maximizeBottomRight(amount);
877 }
878 }
879 onPushStopped: {
880 if (previewRectangle) {
881 previewRectangle.stop();
882 }
883 }
885 onMouseMoved: {
886 mouseNeverMoved = false;
887 cursor.opacity = 1;
888 }
890 Behavior on opacity { UbuntuNumberAnimation {} }
891 }
893 // non-visual objects
894 KeymapSwitcher {
895 focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
896 }
897 BrightnessControl {}
899 Rectangle {
900 id: shutdownFadeOutRectangle
901 z: cursor.z + 1
902 enabled: false
903 visible: false
904 color: "black"
905 anchors.fill: parent
906 opacity: 0.0
907 NumberAnimation on opacity {
908 id: shutdownFadeOut
909 from: 0.0
910 to: 1.0
911 onStopped: {
912 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
913 DBusUnitySessionService.shutdown();
914 }
915 }
916 }
917 }