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.visible && 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 (shell.mode === "greeter" ? screenWindow.active : 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]
523 background: wallpaperResolver.resolvedImage
524 backgroundSourceSize: shell.largestScreenDimension
529 // See powerConnection for why this is useful
530 id: showGreeterDelayed
533 // Go through the dbus service, because it has checks for whether
534 // we are even allowed to lock or not.
535 DBusLomiriSessionService.PromptLock();
543 function onHasCallsChanged() {
544 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "lomiri-dialer-app") {
545 // We just received an incoming call while locked. The
546 // indicator will have already launched lomiri-dialer-app for
547 // us, but there is a race between "hasCalls" changing and the
548 // dialer starting up. So in case we lose that race, we'll
549 // start/focus the dialer ourselves here too. Even if the
550 // indicator didn't launch the dialer for some reason (or maybe
551 // a call started via some other means), if an active call is
552 // happening, we want to be in the dialer.
553 startLockedApp("lomiri-dialer-app")
562 function onStatusChanged(reason) {
563 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
564 !callManager.hasCalls && !wizard.active) {
565 // We don't want to simply call greeter.showNow() here, because
566 // that will take too long. Qt will delay button event
567 // handling until the greeter is done loading and may think the
568 // user held down the power button the whole time, leading to a
569 // power dialog being shown. Instead, delay showing the
570 // greeter until we've finished handling the event. We could
571 // make the greeter load asynchronously instead, but that
572 // introduces a whole host of timing issues, especially with
573 // its animations. So this is simpler.
574 showGreeterDelayed.start();
579 function showHome() {
580 greeter.notifyUserRequestedApp();
582 if (shell.mode === "greeter") {
583 SessionBroadcast.requestHomeShown(AccountsService.user);
585 if (!greeter.active) {
586 launcher.toggleDrawer(false);
588 greeterLoader.toggleDrawerAfterUnlock = true;
600 objectName: "fullscreenSwipeDown"
601 enabled: panel.state === "offscreen"
602 direction: SwipeArea.Downwards
603 immediateRecognition: false
612 panel.temporarilyShow()
617 // TODO: remove when we no longer have With/WithoutCutouts split, this just prevents warnings
618 // about assigning to a property that doesn't exist if the WithoutCutouts variant is loaded
619 // FIXME: use attached properties instead of huge blocks of passthrough properties
622 property: "orientation"
623 value: shell.orientation
624 when: panel.hasOwnProperty("orientation")
630 anchors.fill: parent //because this draws indicator menus
631 blurSource: shell.blurSource
632 z: screenshotEditor.visible ? screenshotEditorContainer.z + 1 : 0
633 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
634 minimizedPanelHeight: shell.screenIndex === 0
635 && shell.orientation === Qt.PortraitOrientation
636 && deviceConfig.collapsedPanelHeight
638 expandedPanelHeight: units.gu(7)
639 applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
640 screenIndex: shell.screenIndex
644 available: tutorial.panelEnabled
645 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
646 && (!greeter || !greeter.hasLockedApp)
647 && !shell.waitingOnGreeter
648 && settings.enableIndicatorMenu
649 && !screenshotEditor.visible
651 model: Indicators.IndicatorsModel {
653 // tablet and phone both use the same profile
654 // FIXME: use just "phone" for greeter too, but first fix
655 // greeter app launching to either load the app inside the
656 // greeter or tell the session to load the app. This will
657 // involve taking the url-dispatcher dbus name and using
658 // SessionBroadcast to tell the session.
659 // For now indicators will just hide their buttons that
660 // usually spawn lomiri-system-settings, based on the
661 // active username being 'lightdm'.
662 profile: shell.mode === "greeter"
663 ? ((shell.usageScenario === "phone" || shell.usageScenario === "tablet")
664 ? "phone_greeter" : "desktop_greeter")
666 Component.onCompleted: {
674 available: (!greeter || !greeter.shown)
675 && !shell.waitingOnGreeter
676 && !stage.spreadShown
677 && !screenshotEditor.visible
680 readonly property bool focusedSurfaceIsFullscreen: shell.topLevelSurfaceList.focusedWindow
681 ? shell.topLevelSurfaceList.focusedWindow.state == Mir.FullscreenState
683 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
684 || greeter.hasLockedApp
685 greeterShown: greeter && greeter.shown
686 hasKeyboard: shell.hasKeyboard
687 panelState: panelState
688 supportsMultiColorLed: shell.supportsMultiColorLed
693 objectName: "launcher"
695 anchors.top: parent.top
696 anchors.topMargin: panel.panelHeight
697 anchors.bottom: parent.bottom
699 dragAreaWidth: shell.edgeSize
700 available: tutorial.launcherEnabled
701 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
702 && !greeter.hasLockedApp
703 && !shell.waitingOnGreeter
704 && shell.mode !== "greeter"
705 && !screenshotEditor.visible
706 visible: shell.mode !== "greeter"
707 inverted: shell.usageScenario !== "desktop"
708 superPressed: physicalKeysMapper.superPressed
709 superTabPressed: physicalKeysMapper.superTabPressed
710 panelWidth: units.gu(settings.launcherWidth)
711 lockedVisible: (lockedByUser || shell.atDesktop) && lockAllowed
712 blurSource: shell.blurSource
713 topPanelHeight: panel.panelHeight
714 drawerEnabled: !greeter.active && tutorial.launcherLongSwipeEnabled
715 privateMode: greeter.active
716 background: wallpaperResolver.resolvedImage
718 // It can be assumed that the Launcher and Panel would overlap if
719 // the Panel is open and taking up the full width of the shell
720 readonly property bool collidingWithPanel: panel && (!panel.fullyClosed && !panel.partialWidth)
722 // The "autohideLauncher" setting is only valid in desktop mode
723 readonly property bool lockedByUser: (shell.usageScenario == "desktop" && !settings.autohideLauncher)
725 // The Launcher should absolutely not be locked visible under some
727 readonly property bool lockAllowed: !collidingWithPanel && !panel.fullscreenMode && !wizard.active && !tutorial.demonstrateLauncher
729 onShowDashHome: showHome()
730 onLauncherApplicationSelected: {
731 greeter.notifyUserRequestedApp();
732 shell.activateApplication(appId);
736 panel.indicators.hide();
737 panel.applicationMenus.hide();
740 onDrawerShownChanged: {
742 panel.indicators.hide();
743 panel.applicationMenus.hide();
753 shortcut: Qt.MetaModifier | Qt.Key_A
755 launcher.toggleDrawer(true);
759 shortcut: Qt.AltModifier | Qt.Key_F1
761 launcher.openForKeyboardNavigation();
765 shortcut: Qt.MetaModifier | Qt.Key_0
767 if (LauncherModel.get(9)) {
768 activateApplication(LauncherModel.get(9).appId);
775 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
777 if (LauncherModel.get(index)) {
778 activateApplication(LauncherModel.get(index).appId);
785 KeyboardShortcutsOverlay {
786 objectName: "shortcutsOverlay"
787 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
788 && height < parent.height - padding - panel.panelHeight
789 anchors.centerIn: parent
790 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
791 anchors.verticalCenterOffset: panel.panelHeight/2
793 opacity: enabled ? 0.95 : 0
795 Behavior on opacity {
796 LomiriNumberAnimation {}
802 objectName: "tutorial"
805 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
806 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
807 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
808 inputMethod.visible ||
809 (launcher.shown && !launcher.lockedVisible) ||
810 panel.indicators.shown || stage.rightEdgeDragProgress > 0
811 usageScenario: shell.usageScenario
812 lastInputTimestamp: inputFilter.lastInputTimestamp
822 deferred: shell.mode === "greeter"
824 function unlockWhenDoneWithWizard() {
825 if (!active && shell.mode !== "greeter") {
826 ModemConnectivity.unlockAllModems();
830 Component.onCompleted: unlockWhenDoneWithWizard()
831 onActiveChanged: unlockWhenDoneWithWizard()
834 MouseArea { // modal notifications prevent interacting with other contents
836 visible: notifications.useModal
843 model: NotificationBackend.Model
845 hasMouse: shell.hasMouse
846 background: wallpaperResolver.resolvedImage
847 privacyMode: greeter.locked && AccountsService.hideNotificationContentWhileLocked
849 y: topmostIsFullscreen ? 0 : panel.panelHeight
850 z: screenshotEditor.visible ? screenshotEditorContainer.z + 1 : 0
851 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
856 when: overlay.width <= units.gu(60)
858 target: notifications
859 anchors.left: parent.left
860 anchors.right: parent.right
865 when: overlay.width > units.gu(60)
867 target: notifications
868 anchors.left: undefined
869 anchors.right: parent.right
871 PropertyChanges { target: notifications; width: units.gu(38) }
878 enabled: !greeter.shown
880 // NB: it does its own positioning according to the specified edge
884 panel.indicators.hide()
887 material: Component {
893 anchors.centerIn: parent
895 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
896 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
904 id: screenshotEditorContainer
912 anchors.topMargin: panel.panelHeight
913 enabled: !wizard.active
915 // Always hide the Launcher and Panel
917 console.log("Showing screenshot editor.");
920 panel.indicators.hide();
921 panel.applicationMenus.hide();
923 screenshotEditorContainer.visible = true;
927 screenshotEditorContainer.visible = false;
930 // Make locking the screen abort the editing session, otherwise we
931 // would show the editor above the lockscreen.
934 function onLockedChanged() {
935 if (!screenshotEditor.visible)
939 screenshotEditor.hide()
948 objectName: "dialogs"
950 visible: hasActiveDialog
952 usageScenario: shell.usageScenario
953 hasKeyboard: shell.hasKeyboard
955 shutdownFadeOutRectangle.enabled = true;
956 shutdownFadeOutRectangle.visible = true;
957 shutdownFadeOut.start();
962 target: SessionBroadcast
963 function onShowHome() { if (shell.mode !== "greeter") showHome() }
968 objectName: "urlDispatcher"
969 active: shell.mode === "greeter"
970 onUrlRequested: shell.activateURL(url)
977 enabled: shell.mode !== "greeter"
978 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
981 ignoreUnknownSignals: true
982 function onItemSnapshotRequested(item) { itemGrabber.capture(item) }
985 if (!settings.enableScreenshotEditor)
988 screenshotEditor.show(path)
993 id: cursorHidingTimer
995 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
996 onTriggered: cursor.opacity = 0;
1001 objectName: "cursor"
1003 z: itemGrabber.z + 1
1004 topBoundaryOffset: panel.panelHeight
1005 enabled: shell.hasMouse && screenWindow.active
1008 property bool mouseNeverMoved: true
1010 target: cursor; property: "x"; value: shell.width / 2
1011 restoreMode: Binding.RestoreBinding
1012 when: cursor.mouseNeverMoved && cursor.visible
1015 target: cursor; property: "y"; value: shell.height / 2
1016 restoreMode: Binding.RestoreBinding
1017 when: cursor.mouseNeverMoved && cursor.visible
1020 confiningItem: stage.itemConfiningMouseCursor
1024 readonly property var previewRectangle: stage.previewRectangle.target &&
1025 stage.previewRectangle.target.dragging ?
1026 stage.previewRectangle : null
1028 onPushedLeftBoundary: {
1029 if (buttons === Qt.NoButton) {
1030 launcher.pushEdge(amount);
1031 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
1032 previewRectangle.maximizeLeft(amount);
1036 onPushedRightBoundary: {
1037 if (buttons === Qt.NoButton) {
1038 rightEdgeBarrier.push(amount);
1039 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
1040 previewRectangle.maximizeRight(amount);
1044 onPushedTopBoundary: {
1045 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
1046 previewRectangle.maximize(amount);
1049 onPushedTopLeftCorner: {
1050 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
1051 previewRectangle.maximizeTopLeft(amount);
1054 onPushedTopRightCorner: {
1055 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
1056 previewRectangle.maximizeTopRight(amount);
1059 onPushedBottomLeftCorner: {
1060 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
1061 previewRectangle.maximizeBottomLeft(amount);
1064 onPushedBottomRightCorner: {
1065 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
1066 previewRectangle.maximizeBottomRight(amount);
1070 if (previewRectangle) {
1071 previewRectangle.stop();
1075 onScreenEntered: screenWindow.screen.active = true
1078 mouseNeverMoved = false;
1082 Behavior on opacity { LomiriNumberAnimation {} }
1086 anchors.fill: parent
1090 acceptedDevices: PointerDevice.TouchScreen
1091 onActiveChanged: if (active) screenWindow.screen.active = true
1095 // non-visual objects
1097 focusedSurface: shell.topLevelSurfaceList.focusedWindow ? shell.topLevelSurfaceList.focusedWindow.surface : null
1099 BrightnessControl {}
1102 id: shutdownFadeOutRectangle
1107 anchors.fill: parent
1109 NumberAnimation on opacity {
1114 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
1115 DBusLomiriSessionService.shutdown();