2 * Copyright (C) 2013-2016 Canonical Ltd.
3 * Copyright (C) 2019-2021 UBports Foundation
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.
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.
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/>.
20import QtQuick.Window 2.2
21import AccountsService 0.1
22import QtMir.Application 0.1
23import Lomiri.Components 1.3
24import Lomiri.Components.Popups 1.3
25import Lomiri.Gestures 0.1
26import Lomiri.Telephony 0.1 as Telephony
27import Lomiri.ModemConnectivity 0.1
28import Lomiri.Launcher 0.1
29import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
33import SessionBroadcast 0.1
42import "Components/PanelState"
43import Lomiri.Notifications 1.0 as NotificationBackend
44import Lomiri.Session 0.1
45import Lomiri.Indicators 0.1 as Indicators
47import WindowManager 1.0
57 readonly property bool lightMode: settings.lightMode
58 theme.name: lightMode ? "Lomiri.Components.Themes.Ambiance" :
59 "Lomiri.Components.Themes.SuruDark"
61 // to be set from outside
62 property int orientationAngle: 0
63 property int orientation
64 property Orientations orientations
65 property real nativeWidth
66 property real nativeHeight
67 property alias panelAreaShowProgress: panel.panelAreaShowProgress
68 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
69 property string mode: "full-greeter"
70 property int screenIndex: -1
71 property alias oskEnabled: inputMethod.enabled
72 function updateFocusedAppOrientation() {
73 stage.updateFocusedAppOrientation();
75 function updateFocusedAppOrientationAnimated() {
76 stage.updateFocusedAppOrientationAnimated();
78 property bool hasMouse: false
79 property bool hasKeyboard: false
80 property bool hasTouchscreen: false
81 property bool supportsMultiColorLed: true
83 // The largest dimension, in pixels, of all of the screens this Shell is
85 // If a script sets the shell to 240x320 when it was 320x240, we could
86 // end up in a situation where our dimensions are 240x240 for a short time.
87 // Notifying the Wallpaper of both events would make it reload the image
88 // twice. So, we use a Binding { delayed: true }.
89 property real largestScreenDimension
92 restoreMode: Binding.RestoreBinding
94 property: "largestScreenDimension"
95 value: Math.max(nativeWidth, nativeHeight)
99 property alias lightIndicators: indicatorsModel.light
101 // to be read from outside
102 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
104 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
105 && stage.orientationChangesEnabled
106 && (!greeter.animating)
108 readonly property bool showingGreeter: greeter && greeter.shown
110 property bool startingUp: true
111 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
113 property int supportedOrientations: {
115 // Ensure we don't rotate during start up
116 return Qt.PrimaryOrientation;
117 } else if (notifications.topmostIsFullscreen) {
118 return Qt.PrimaryOrientation;
120 return shell.orientations ? shell.orientations.map(stage.supportedOrientations) : Qt.PrimaryOrientation;
124 readonly property var mainApp: stage.mainApp
126 readonly property var topLevelSurfaceList: {
127 if (!WMScreen.currentWorkspace) return null;
128 return stage.temporarySelectedWorkspace ? stage.temporarySelectedWorkspace.windowModel : WMScreen.currentWorkspace.windowModel
132 _onMainAppChanged((mainApp ? mainApp.appId : ""));
135 target: ApplicationManager
136 function onFocusRequested(appId) {
137 if (shell.mainApp && shell.mainApp.appId === appId) {
138 _onMainAppChanged(appId);
143 // Calls attention back to the most important thing that's been focused
144 // (ex: phone calls go over Wizard, app focuses go over indicators, greeter
145 // goes over everything if it is locked)
146 // Must be called whenever app focus changes occur, even if the focus change
147 // is "nothing is focused". In that case, call with appId = ""
148 function _onMainAppChanged(appId) {
152 // If this happens on first boot, we may be in the
153 // wizard while receiving a call. A call is more
154 // important than the wizard so just bail out of it.
158 if (appId === "lomiri-dialer-app" && callManager.hasCalls && greeter.locked) {
159 // If we are in the middle of a call, make dialer lockedApp. The
160 // Greeter will show it when it's notified of the focus.
161 // This can happen if user backs out of dialer back to greeter, then
162 // launches dialer again.
163 greeter.lockedApp = appId;
166 panel.indicators.hide();
167 launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
170 // *Always* make sure the greeter knows that the focused app changed
171 if (greeter) greeter.notifyAppFocusRequested(appId);
174 // For autopilot consumption
175 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
177 // Note when greeter is waiting on PAM, so that we can disable edges until
178 // we know which user data to show and whether the session is locked.
179 readonly property bool waitingOnGreeter: greeter && greeter.waiting
181 // True when the user is logged in with no apps running
182 readonly property bool atDesktop: topLevelSurfaceList && greeter && topLevelSurfaceList.count === 0 && !greeter.active
184 onAtDesktopChanged: {
185 if (atDesktop && stage && !stage.workspaceEnabled) {
190 property real edgeSize: units.gu(settings.edgeDragWidth)
193 id: wallpaperResolver
194 objectName: "wallpaperResolver"
196 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
197 readonly property bool hasCustomBackground: resolvedImage != defaultBackground
198 readonly property string gsettingsBackgroundPictureUri: ((shell.showingGreeter == true)
199 || (shell.mode === "full-greeter")
200 || (shell.mode === "greeter"))
201 ? backgroundGreeterSettings.backgroundPictureUri
202 : backgroundShellSettings.backgroundPictureUri
205 id: backgroundShellSettings
206 schema.id: "com.lomiri.Shell"
209 id: backgroundGreeterSettings
210 schema.id: "com.lomiri.Shell.Greeter"
214 AccountsService.backgroundFile,
215 gsettingsBackgroundPictureUri,
220 readonly property alias greeter: greeterLoader.item
222 readonly property var blurSource: {
223 if (!settings.enableBlur)
226 if (screenshotEditorContainer.visible)
227 return screenshotEditorContainer;
235 function activateApplication(appId) {
236 topLevelSurfaceList.pendingActivation();
238 // Either open the app in our own session, or -- if we're acting as a
239 // greeter -- ask the user's session to open it for us.
240 if (shell.mode === "greeter") {
241 activateURL("application:///" + appId + ".desktop");
248 function activateURL(url) {
249 SessionBroadcast.requestUrlStart(AccountsService.user, url);
250 greeter.notifyUserRequestedApp();
251 panel.indicators.hide();
254 function startApp(appId) {
255 if (!ApplicationManager.findApplication(appId)) {
256 ApplicationManager.startApplication(appId);
258 ApplicationManager.requestFocusApplication(appId);
261 function startLockedApp(app) {
262 topLevelSurfaceList.pendingActivation();
264 if (greeter.locked) {
265 greeter.lockedApp = app;
267 startApp(app); // locked apps are always in our same session
271 target: LauncherModel
272 restoreMode: Binding.RestoreBinding
273 property: "applicationManager"
274 value: ApplicationManager
277 Component.onCompleted: {
278 finishStartUpTimer.start();
286 visible: Powerd.highBrightnessModeEnabled
288 /* Dimmer overlay must cover everything. */
293 id: physicalKeysMapper
294 objectName: "physicalKeysMapper"
296 onPowerKeyLongPressed: dialogs.showPowerDialog();
297 onVolumeDownTriggered: volumeControl.volumeDown();
298 onVolumeUpTriggered: volumeControl.volumeUp();
299 onScreenshotTriggered: itemGrabber.capture(shell);
303 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
308 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
309 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
313 objectName: "windowInputMonitor"
314 onHomeKeyActivated: {
315 // Ignore when greeter is active, to avoid pocket presses
316 if (!greeter.active) {
317 launcher.toggleDrawer(/* focusInputField */ false,
318 /* onlyOpen */ false,
319 /* alsoToggleLauncher */ true);
322 onTouchBegun: { cursor.opacity = 0; }
324 // move the (hidden) cursor to the last known touch position
325 var mappedCoords = mapFromItem(null, pos.x, pos.y);
326 cursor.x = mappedCoords.x;
327 cursor.y = mappedCoords.y;
328 cursor.mouseNeverMoved = false;
332 AvailableDesktopArea {
333 id: availableDesktopAreaItem
335 anchors.topMargin: panel.fullscreenMode ? 0 : panel.minimizedPanelHeight
336 anchors.leftMargin: (launcher.lockedByUser && launcher.lockAllowed) ? launcher.panelWidth : 0
341 schema.id: "com.lomiri.Shell"
346 objectName: "panelState"
353 height: parent.height
361 dragAreaWidth: shell.edgeSize
362 background: wallpaperResolver.resolvedImage
363 backgroundSourceSize: shell.largestScreenDimension
365 applicationManager: ApplicationManager
366 topLevelSurfaceList: shell.topLevelSurfaceList
367 inputMethodRect: inputMethod.visibleRect
368 rightEdgePushProgress: rightEdgeBarrier.progress
369 availableDesktopArea: availableDesktopAreaItem
370 launcherLeftMargin: launcher.visibleWidth
372 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
374 : shell.usageScenario
376 mode: usageScenario == "phone" ? "staged"
377 : usageScenario == "tablet" ? "stagedWithSideStage"
380 shellOrientation: shell.orientation
381 shellOrientationAngle: shell.orientationAngle
382 orientations: shell.orientations
383 nativeWidth: shell.nativeWidth
384 nativeHeight: shell.nativeHeight
386 allowInteractivity: (!greeter || !greeter.shown)
387 && panel.indicators.fullyClosed
388 && !notifications.useModal
389 && !launcher.takesFocus
391 suspended: greeter.shown
392 altTabPressed: physicalKeysMapper.altTabPressed
393 oskEnabled: shell.oskEnabled
394 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
395 panelState: panelState
397 onSpreadShownChanged: {
398 panel.indicators.hide();
399 panel.applicationMenus.hide();
407 minimumTouchPoints: 4
408 maximumTouchPoints: minimumTouchPoints
410 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
411 touchPoints.length >= minimumTouchPoints &&
412 touchPoints.length <= maximumTouchPoints
413 property bool wasPressed: false
415 onRecognisedPressChanged: {
416 if (recognisedPress) {
422 if (status !== TouchGestureArea.Recognized) {
423 if (status === TouchGestureArea.WaitingForTouch) {
424 if (wasPressed && !dragging) {
425 launcher.toggleDrawer(true);
436 objectName: "inputMethod"
439 topMargin: panel.panelHeight
440 leftMargin: (launcher.lockedByUser && launcher.lockAllowed) ? launcher.panelWidth : 0
442 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
447 objectName: "greeterLoader"
450 if (shell.mode != "shell") {
451 if (screenWindow.primary) return integratedGreeter;
452 return secondaryGreeter;
454 return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
457 item.objectName = "greeter"
459 property bool toggleDrawerAfterUnlock: false
462 function onActiveChanged() {
466 // Show drawer in case showHome() requests it
467 if (greeterLoader.toggleDrawerAfterUnlock) {
468 launcher.toggleDrawer(false);
469 greeterLoader.toggleDrawerAfterUnlock = false;
478 id: integratedGreeter
481 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
482 hides: [launcher, panel.indicators, panel.applicationMenus]
483 tabletMode: shell.usageScenario != "phone"
484 usageMode: shell.usageScenario
485 orientation: shell.orientation
486 forcedUnlock: wizard.active || shell.mode === "full-shell"
487 background: wallpaperResolver.resolvedImage
488 backgroundSourceSize: shell.largestScreenDimension
489 hasCustomBackground: wallpaperResolver.hasCustomBackground
490 inputMethodRect: inputMethod.visibleRect
491 hasKeyboard: shell.hasKeyboard
492 allowFingerprint: !dialogs.hasActiveDialog &&
493 !notifications.topmostIsFullscreen &&
494 !panel.indicators.shown
495 panelHeight: panel.panelHeight
497 // avoid overlapping with Launcher's edge drag area
498 // FIXME: Fix TouchRegistry & friends and remove this workaround
499 // Issue involves launcher's DDA getting disabled on a long
501 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
504 if (!tutorial.running) {
509 onEmergencyCall: startLockedApp("lomiri-dialer-app")
511 // Quit the greeter as soon as a session has been started
513 if (shell.mode == "greeter")
522 hides: [launcher, panel.indicators]
527 // See powerConnection for why this is useful
528 id: showGreeterDelayed
531 // Go through the dbus service, because it has checks for whether
532 // we are even allowed to lock or not.
533 DBusLomiriSessionService.PromptLock();
541 function onHasCallsChanged() {
542 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "lomiri-dialer-app") {
543 // We just received an incoming call while locked. The
544 // indicator will have already launched lomiri-dialer-app for
545 // us, but there is a race between "hasCalls" changing and the
546 // dialer starting up. So in case we lose that race, we'll
547 // start/focus the dialer ourselves here too. Even if the
548 // indicator didn't launch the dialer for some reason (or maybe
549 // a call started via some other means), if an active call is
550 // happening, we want to be in the dialer.
551 startLockedApp("lomiri-dialer-app")
560 function onStatusChanged(reason) {
561 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
562 !callManager.hasCalls && !wizard.active) {
563 // We don't want to simply call greeter.showNow() here, because
564 // that will take too long. Qt will delay button event
565 // handling until the greeter is done loading and may think the
566 // user held down the power button the whole time, leading to a
567 // power dialog being shown. Instead, delay showing the
568 // greeter until we've finished handling the event. We could
569 // make the greeter load asynchronously instead, but that
570 // introduces a whole host of timing issues, especially with
571 // its animations. So this is simpler.
572 showGreeterDelayed.start();
577 function showHome() {
578 greeter.notifyUserRequestedApp();
580 if (shell.mode === "greeter") {
581 SessionBroadcast.requestHomeShown(AccountsService.user);
583 if (!greeter.active) {
584 launcher.toggleDrawer(false);
586 greeterLoader.toggleDrawerAfterUnlock = true;
598 objectName: "fullscreenSwipeDown"
599 enabled: panel.state === "offscreen"
600 direction: SwipeArea.Downwards
601 immediateRecognition: false
610 panel.temporarilyShow()
615 // TODO: remove when we no longer have With/WithoutCutouts split, this just prevents warnings
616 // about assigning to a property that doesn't exist if the WithoutCutouts variant is loaded
617 // FIXME: use attached properties instead of huge blocks of passthrough properties
620 property: "orientation"
621 value: shell.orientation
622 when: panel.hasOwnProperty("orientation")
628 anchors.fill: parent //because this draws indicator menus
629 blurSource: shell.blurSource
630 z: screenshotEditor.visible ? screenshotEditorContainer.z + 1 : 0
631 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
632 minimizedPanelHeight: shell.screenIndex === 0
633 && shell.orientation === Qt.PortraitOrientation
634 && deviceConfig.collapsedPanelHeight
636 expandedPanelHeight: units.gu(7)
637 applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
638 screenIndex: shell.screenIndex
642 available: tutorial.panelEnabled
643 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
644 && (!greeter || !greeter.hasLockedApp)
645 && !shell.waitingOnGreeter
646 && settings.enableIndicatorMenu
647 && !screenshotEditor.visible
649 model: Indicators.IndicatorsModel {
651 // tablet and phone both use the same profile
652 // FIXME: use just "phone" for greeter too, but first fix
653 // greeter app launching to either load the app inside the
654 // greeter or tell the session to load the app. This will
655 // involve taking the url-dispatcher dbus name and using
656 // SessionBroadcast to tell the session.
657 // For now indicators will just hide their buttons that
658 // usually spawn lomiri-system-settings, based on the
659 // active username being 'lightdm'.
660 profile: shell.mode === "greeter"
661 ? ((shell.usageScenario === "phone" || shell.usageScenario === "tablet")
662 ? "phone_greeter" : "desktop_greeter")
664 Component.onCompleted: {
672 available: (!greeter || !greeter.shown)
673 && !shell.waitingOnGreeter
674 && !stage.spreadShown
675 && !screenshotEditor.visible
678 readonly property bool focusedSurfaceIsFullscreen: shell.topLevelSurfaceList.focusedWindow
679 ? shell.topLevelSurfaceList.focusedWindow.state == Mir.FullscreenState
681 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
682 || greeter.hasLockedApp
683 greeterShown: greeter && greeter.shown
684 hasKeyboard: shell.hasKeyboard
685 panelState: panelState
686 supportsMultiColorLed: shell.supportsMultiColorLed
691 objectName: "launcher"
693 anchors.top: parent.top
694 anchors.topMargin: panel.panelHeight
695 anchors.bottom: parent.bottom
697 dragAreaWidth: shell.edgeSize
698 available: tutorial.launcherEnabled
699 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
700 && !greeter.hasLockedApp
701 && !shell.waitingOnGreeter
702 && shell.mode !== "greeter"
703 && !screenshotEditor.visible
704 visible: shell.mode !== "greeter"
705 inverted: shell.usageScenario !== "desktop"
706 superPressed: physicalKeysMapper.superPressed
707 superTabPressed: physicalKeysMapper.superTabPressed
708 panelWidth: units.gu(settings.launcherWidth)
709 lockedVisible: (lockedByUser || shell.atDesktop) && lockAllowed
710 blurSource: shell.blurSource
711 topPanelHeight: panel.panelHeight
712 drawerEnabled: !greeter.active && tutorial.launcherLongSwipeEnabled
713 privateMode: greeter.active
714 background: wallpaperResolver.resolvedImage
716 // It can be assumed that the Launcher and Panel would overlap if
717 // the Panel is open and taking up the full width of the shell
718 readonly property bool collidingWithPanel: panel && (!panel.fullyClosed && !panel.partialWidth)
720 // The "autohideLauncher" setting is only valid in desktop mode
721 readonly property bool lockedByUser: (shell.usageScenario == "desktop" && !settings.autohideLauncher)
723 // The Launcher should absolutely not be locked visible under some
725 readonly property bool lockAllowed: !collidingWithPanel && !panel.fullscreenMode && !wizard.active && !tutorial.demonstrateLauncher
727 onShowDashHome: showHome()
728 onLauncherApplicationSelected: {
729 greeter.notifyUserRequestedApp();
730 shell.activateApplication(appId);
734 panel.indicators.hide();
735 panel.applicationMenus.hide();
738 onDrawerShownChanged: {
740 panel.indicators.hide();
741 panel.applicationMenus.hide();
751 shortcut: Qt.MetaModifier | Qt.Key_A
753 launcher.toggleDrawer(true);
757 shortcut: Qt.AltModifier | Qt.Key_F1
759 launcher.openForKeyboardNavigation();
763 shortcut: Qt.MetaModifier | Qt.Key_0
765 if (LauncherModel.get(9)) {
766 activateApplication(LauncherModel.get(9).appId);
773 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
775 if (LauncherModel.get(index)) {
776 activateApplication(LauncherModel.get(index).appId);
783 KeyboardShortcutsOverlay {
784 objectName: "shortcutsOverlay"
785 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
786 && height < parent.height - padding - panel.panelHeight
787 anchors.centerIn: parent
788 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
789 anchors.verticalCenterOffset: panel.panelHeight/2
791 opacity: enabled ? 0.95 : 0
793 Behavior on opacity {
794 LomiriNumberAnimation {}
800 objectName: "tutorial"
803 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
804 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
805 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
806 inputMethod.visible ||
807 (launcher.shown && !launcher.lockedVisible) ||
808 panel.indicators.shown || stage.rightEdgeDragProgress > 0
809 usageScenario: shell.usageScenario
810 lastInputTimestamp: inputFilter.lastInputTimestamp
820 deferred: shell.mode === "greeter"
822 function unlockWhenDoneWithWizard() {
823 if (!active && shell.mode !== "greeter") {
824 ModemConnectivity.unlockAllModems();
828 Component.onCompleted: unlockWhenDoneWithWizard()
829 onActiveChanged: unlockWhenDoneWithWizard()
832 MouseArea { // modal notifications prevent interacting with other contents
834 visible: notifications.useModal
841 model: NotificationBackend.Model
843 hasMouse: shell.hasMouse
844 background: wallpaperResolver.resolvedImage
845 privacyMode: greeter.locked && AccountsService.hideNotificationContentWhileLocked
847 y: topmostIsFullscreen ? 0 : panel.panelHeight
848 z: screenshotEditor.visible ? screenshotEditorContainer.z + 1 : 0
849 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
854 when: overlay.width <= units.gu(60)
856 target: notifications
857 anchors.left: parent.left
858 anchors.right: parent.right
863 when: overlay.width > units.gu(60)
865 target: notifications
866 anchors.left: undefined
867 anchors.right: parent.right
869 PropertyChanges { target: notifications; width: units.gu(38) }
876 enabled: !greeter.shown
878 // NB: it does its own positioning according to the specified edge
882 panel.indicators.hide()
885 material: Component {
891 anchors.centerIn: parent
893 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
894 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
902 id: screenshotEditorContainer
910 anchors.topMargin: panel.panelHeight
911 enabled: !wizard.active
913 // Always hide the Launcher and Panel
915 console.log("Showing screenshot editor.");
918 panel.indicators.hide();
919 panel.applicationMenus.hide();
921 screenshotEditorContainer.visible = true;
925 screenshotEditorContainer.visible = false;
928 // Make locking the screen abort the editing session, otherwise we
929 // would show the editor above the lockscreen.
932 function onLockedChanged() {
933 if (!screenshotEditor.visible)
937 screenshotEditor.hide()
946 objectName: "dialogs"
948 visible: hasActiveDialog
950 usageScenario: shell.usageScenario
951 hasKeyboard: shell.hasKeyboard
953 shutdownFadeOutRectangle.enabled = true;
954 shutdownFadeOutRectangle.visible = true;
955 shutdownFadeOut.start();
960 target: SessionBroadcast
961 function onShowHome() { if (shell.mode !== "greeter") showHome() }
966 objectName: "urlDispatcher"
967 active: shell.mode === "greeter"
968 onUrlRequested: shell.activateURL(url)
975 enabled: shell.mode !== "greeter"
976 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
979 ignoreUnknownSignals: true
980 function onItemSnapshotRequested(item) { itemGrabber.capture(item) }
983 if (!settings.enableScreenshotEditor)
986 screenshotEditor.show(path)
991 id: cursorHidingTimer
993 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
994 onTriggered: cursor.opacity = 0;
1001 z: itemGrabber.z + 1
1002 topBoundaryOffset: panel.panelHeight
1003 enabled: shell.hasMouse && screenWindow.active
1006 property bool mouseNeverMoved: true
1008 target: cursor; property: "x"; value: shell.width / 2
1009 restoreMode: Binding.RestoreBinding
1010 when: cursor.mouseNeverMoved && cursor.visible
1013 target: cursor; property: "y"; value: shell.height / 2
1014 restoreMode: Binding.RestoreBinding
1015 when: cursor.mouseNeverMoved && cursor.visible
1018 confiningItem: stage.itemConfiningMouseCursor
1022 readonly property var previewRectangle: stage.previewRectangle.target &&
1023 stage.previewRectangle.target.dragging ?
1024 stage.previewRectangle : null
1026 onPushedLeftBoundary: {
1027 if (buttons === Qt.NoButton) {
1028 launcher.pushEdge(amount);
1029 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
1030 previewRectangle.maximizeLeft(amount);
1034 onPushedRightBoundary: {
1035 if (buttons === Qt.NoButton) {
1036 rightEdgeBarrier.push(amount);
1037 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
1038 previewRectangle.maximizeRight(amount);
1042 onPushedTopBoundary: {
1043 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
1044 previewRectangle.maximize(amount);
1047 onPushedTopLeftCorner: {
1048 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
1049 previewRectangle.maximizeTopLeft(amount);
1052 onPushedTopRightCorner: {
1053 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
1054 previewRectangle.maximizeTopRight(amount);
1057 onPushedBottomLeftCorner: {
1058 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
1059 previewRectangle.maximizeBottomLeft(amount);
1062 onPushedBottomRightCorner: {
1063 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
1064 previewRectangle.maximizeBottomRight(amount);
1068 if (previewRectangle) {
1069 previewRectangle.stop();
1074 mouseNeverMoved = false;
1078 Behavior on opacity { LomiriNumberAnimation {} }
1081 // non-visual objects
1083 focusedSurface: shell.topLevelSurfaceList.focusedWindow ? shell.topLevelSurfaceList.focusedWindow.surface : null
1085 BrightnessControl {}
1088 id: shutdownFadeOutRectangle
1093 anchors.fill: parent
1095 NumberAnimation on opacity {
1100 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
1101 DBusLomiriSessionService.shutdown();