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 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
853 when: overlay.width <= units.gu(60)
855 target: notifications
856 anchors.left: parent.left
857 anchors.right: parent.right
862 when: overlay.width > units.gu(60)
864 target: notifications
865 anchors.left: undefined
866 anchors.right: parent.right
868 PropertyChanges { target: notifications; width: units.gu(38) }
875 enabled: !greeter.shown
877 // NB: it does its own positioning according to the specified edge
881 panel.indicators.hide()
884 material: Component {
890 anchors.centerIn: parent
892 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
893 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
901 id: screenshotEditorContainer
909 anchors.topMargin: panel.panelHeight
910 enabled: !wizard.active
912 // Always hide the Launcher and Panel
914 console.log("Showing screenshot editor.");
917 panel.indicators.hide();
918 panel.applicationMenus.hide();
920 screenshotEditorContainer.visible = true;
924 screenshotEditorContainer.visible = false;
927 // Make locking the screen abort the editing session, otherwise we
928 // would show the editor above the lockscreen.
931 function onLockedChanged() {
932 if (!screenshotEditor.visible)
936 screenshotEditor.hide()
945 objectName: "dialogs"
947 visible: hasActiveDialog
949 usageScenario: shell.usageScenario
950 hasKeyboard: shell.hasKeyboard
952 shutdownFadeOutRectangle.enabled = true;
953 shutdownFadeOutRectangle.visible = true;
954 shutdownFadeOut.start();
959 target: SessionBroadcast
960 function onShowHome() { if (shell.mode !== "greeter") showHome() }
965 objectName: "urlDispatcher"
966 active: shell.mode === "greeter"
967 onUrlRequested: shell.activateURL(url)
974 enabled: shell.mode !== "greeter"
975 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
978 ignoreUnknownSignals: true
979 function onItemSnapshotRequested(item) { itemGrabber.capture(item) }
982 if (!settings.enableScreenshotEditor)
985 screenshotEditor.show(path)
990 id: cursorHidingTimer
992 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
993 onTriggered: cursor.opacity = 0;
1000 z: itemGrabber.z + 1
1001 topBoundaryOffset: panel.panelHeight
1002 enabled: shell.hasMouse && screenWindow.active
1005 property bool mouseNeverMoved: true
1007 target: cursor; property: "x"; value: shell.width / 2
1008 restoreMode: Binding.RestoreBinding
1009 when: cursor.mouseNeverMoved && cursor.visible
1012 target: cursor; property: "y"; value: shell.height / 2
1013 restoreMode: Binding.RestoreBinding
1014 when: cursor.mouseNeverMoved && cursor.visible
1017 confiningItem: stage.itemConfiningMouseCursor
1021 readonly property var previewRectangle: stage.previewRectangle.target &&
1022 stage.previewRectangle.target.dragging ?
1023 stage.previewRectangle : null
1025 onPushedLeftBoundary: {
1026 if (buttons === Qt.NoButton) {
1027 launcher.pushEdge(amount);
1028 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
1029 previewRectangle.maximizeLeft(amount);
1033 onPushedRightBoundary: {
1034 if (buttons === Qt.NoButton) {
1035 rightEdgeBarrier.push(amount);
1036 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
1037 previewRectangle.maximizeRight(amount);
1041 onPushedTopBoundary: {
1042 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
1043 previewRectangle.maximize(amount);
1046 onPushedTopLeftCorner: {
1047 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
1048 previewRectangle.maximizeTopLeft(amount);
1051 onPushedTopRightCorner: {
1052 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
1053 previewRectangle.maximizeTopRight(amount);
1056 onPushedBottomLeftCorner: {
1057 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
1058 previewRectangle.maximizeBottomLeft(amount);
1061 onPushedBottomRightCorner: {
1062 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
1063 previewRectangle.maximizeBottomRight(amount);
1067 if (previewRectangle) {
1068 previewRectangle.stop();
1073 mouseNeverMoved = false;
1077 Behavior on opacity { LomiriNumberAnimation {} }
1080 // non-visual objects
1082 focusedSurface: shell.topLevelSurfaceList.focusedWindow ? shell.topLevelSurfaceList.focusedWindow.surface : null
1084 BrightnessControl {}
1087 id: shutdownFadeOutRectangle
1092 anchors.fill: parent
1094 NumberAnimation on opacity {
1099 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
1100 DBusLomiriSessionService.shutdown();