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
53 readonly property bool lightMode: settings.lightMode
54 theme.name: lightMode ? "Lomiri.Components.Themes.Ambiance" :
55 "Lomiri.Components.Themes.SuruDark"
57 // to be set from outside
58 property int orientationAngle: 0
59 property int orientation
60 property Orientations orientations
61 property real nativeWidth
62 property real nativeHeight
63 property alias panelAreaShowProgress: panel.panelAreaShowProgress
64 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
65 property string mode: "full-greeter"
66 property alias oskEnabled: inputMethod.enabled
67 function updateFocusedAppOrientation() {
68 stage.updateFocusedAppOrientation();
70 function updateFocusedAppOrientationAnimated() {
71 stage.updateFocusedAppOrientationAnimated();
73 property bool hasMouse: false
74 property bool hasKeyboard: false
75 property bool hasTouchscreen: false
76 property bool supportsMultiColorLed: true
78 // The largest dimension, in pixels, of all of the screens this Shell is
80 // If a script sets the shell to 240x320 when it was 320x240, we could
81 // end up in a situation where our dimensions are 240x240 for a short time.
82 // Notifying the Wallpaper of both events would make it reload the image
83 // twice. So, we use a Binding { delayed: true }.
84 property real largestScreenDimension
87 restoreMode: Binding.RestoreBinding
89 property: "largestScreenDimension"
90 value: Math.max(nativeWidth, nativeHeight)
94 property alias lightIndicators: indicatorsModel.light
96 // to be read from outside
97 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
99 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
100 && stage.orientationChangesEnabled
101 && (!greeter.animating)
103 readonly property bool showingGreeter: greeter && greeter.shown
105 property bool startingUp: true
106 Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
108 property int supportedOrientations: {
110 // Ensure we don't rotate during start up
111 return Qt.PrimaryOrientation;
112 } else if (notifications.topmostIsFullscreen) {
113 return Qt.PrimaryOrientation;
115 return shell.orientations ? shell.orientations.map(stage.supportedOrientations) : Qt.PrimaryOrientation;
119 readonly property var mainApp: stage.mainApp
121 readonly property var topLevelSurfaceList: {
122 if (!WMScreen.currentWorkspace) return null;
123 return stage.temporarySelectedWorkspace ? stage.temporarySelectedWorkspace.windowModel : WMScreen.currentWorkspace.windowModel
127 _onMainAppChanged((mainApp ? mainApp.appId : ""));
130 target: ApplicationManager
131 function onFocusRequested(appId) {
132 if (shell.mainApp && shell.mainApp.appId === appId) {
133 _onMainAppChanged(appId);
138 // Calls attention back to the most important thing that's been focused
139 // (ex: phone calls go over Wizard, app focuses go over indicators, greeter
140 // goes over everything if it is locked)
141 // Must be called whenever app focus changes occur, even if the focus change
142 // is "nothing is focused". In that case, call with appId = ""
143 function _onMainAppChanged(appId) {
147 // If this happens on first boot, we may be in the
148 // wizard while receiving a call. A call is more
149 // important than the wizard so just bail out of it.
153 if (appId === "lomiri-dialer-app" && callManager.hasCalls && greeter.locked) {
154 // If we are in the middle of a call, make dialer lockedApp. The
155 // Greeter will show it when it's notified of the focus.
156 // This can happen if user backs out of dialer back to greeter, then
157 // launches dialer again.
158 greeter.lockedApp = appId;
161 panel.indicators.hide();
162 launcher.hide(launcher.ignoreHideIfMouseOverLauncher);
165 // *Always* make sure the greeter knows that the focused app changed
166 if (greeter) greeter.notifyAppFocusRequested(appId);
169 // For autopilot consumption
170 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
172 // Note when greeter is waiting on PAM, so that we can disable edges until
173 // we know which user data to show and whether the session is locked.
174 readonly property bool waitingOnGreeter: greeter && greeter.waiting
176 // True when the user is logged in with no apps running
177 readonly property bool atDesktop: topLevelSurfaceList && greeter && topLevelSurfaceList.count === 0 && !greeter.active
179 onAtDesktopChanged: {
180 if (atDesktop && stage && !stage.workspaceEnabled) {
185 property real edgeSize: units.gu(settings.edgeDragWidth)
188 id: wallpaperResolver
189 objectName: "wallpaperResolver"
191 readonly property url defaultBackground: "file://" + Constants.defaultWallpaper
192 readonly property bool hasCustomBackground: resolvedImage != defaultBackground
193 readonly property string gsettingsBackgroundPictureUri: ((shell.showingGreeter == true)
194 || (shell.mode === "full-greeter")
195 || (shell.mode === "greeter"))
196 ? backgroundGreeterSettings.backgroundPictureUri
197 : backgroundShellSettings.backgroundPictureUri
200 id: backgroundShellSettings
201 schema.id: "com.lomiri.Shell"
204 id: backgroundGreeterSettings
205 schema.id: "com.lomiri.Shell.Greeter"
209 AccountsService.backgroundFile,
210 gsettingsBackgroundPictureUri,
215 readonly property alias greeter: greeterLoader.item
217 function activateApplication(appId) {
218 topLevelSurfaceList.pendingActivation();
220 // Either open the app in our own session, or -- if we're acting as a
221 // greeter -- ask the user's session to open it for us.
222 if (shell.mode === "greeter") {
223 activateURL("application:///" + appId + ".desktop");
230 function activateURL(url) {
231 SessionBroadcast.requestUrlStart(AccountsService.user, url);
232 greeter.notifyUserRequestedApp();
233 panel.indicators.hide();
236 function startApp(appId) {
237 if (!ApplicationManager.findApplication(appId)) {
238 ApplicationManager.startApplication(appId);
240 ApplicationManager.requestFocusApplication(appId);
243 function startLockedApp(app) {
244 topLevelSurfaceList.pendingActivation();
246 if (greeter.locked) {
247 greeter.lockedApp = app;
249 startApp(app); // locked apps are always in our same session
253 target: LauncherModel
254 restoreMode: Binding.RestoreBinding
255 property: "applicationManager"
256 value: ApplicationManager
259 Component.onCompleted: {
260 finishStartUpTimer.start();
268 id: physicalKeysMapper
269 objectName: "physicalKeysMapper"
271 onPowerKeyLongPressed: dialogs.showPowerDialog();
272 onVolumeDownTriggered: volumeControl.volumeDown();
273 onVolumeUpTriggered: volumeControl.volumeUp();
274 onScreenshotTriggered: itemGrabber.capture(shell);
278 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
283 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
284 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
288 objectName: "windowInputMonitor"
289 onHomeKeyActivated: {
290 // Ignore when greeter is active, to avoid pocket presses
291 if (!greeter.active) {
292 launcher.toggleDrawer(/* focusInputField */ false,
293 /* onlyOpen */ false,
294 /* alsoToggleLauncher */ true);
297 onTouchBegun: { cursor.opacity = 0; }
299 // move the (hidden) cursor to the last known touch position
300 var mappedCoords = mapFromItem(null, pos.x, pos.y);
301 cursor.x = mappedCoords.x;
302 cursor.y = mappedCoords.y;
303 cursor.mouseNeverMoved = false;
307 AvailableDesktopArea {
308 id: availableDesktopAreaItem
310 anchors.topMargin: panel.fullscreenMode ? 0 : panel.minimizedPanelHeight
311 anchors.leftMargin: (launcher.lockedByUser && launcher.lockAllowed) ? launcher.panelWidth : 0
316 schema.id: "com.lomiri.Shell"
321 objectName: "panelState"
328 height: parent.height
335 lightMode: shell.lightMode
337 dragAreaWidth: shell.edgeSize
338 background: wallpaperResolver.resolvedImage
339 backgroundSourceSize: shell.largestScreenDimension
341 applicationManager: ApplicationManager
342 topLevelSurfaceList: shell.topLevelSurfaceList
343 inputMethodRect: inputMethod.visibleRect
344 rightEdgePushProgress: rightEdgeBarrier.progress
345 availableDesktopArea: availableDesktopAreaItem
346 launcherLeftMargin: launcher.visibleWidth
348 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
350 : shell.usageScenario
352 mode: usageScenario == "phone" ? "staged"
353 : usageScenario == "tablet" ? "stagedWithSideStage"
356 shellOrientation: shell.orientation
357 shellOrientationAngle: shell.orientationAngle
358 orientations: shell.orientations
359 nativeWidth: shell.nativeWidth
360 nativeHeight: shell.nativeHeight
362 allowInteractivity: (!greeter || !greeter.shown)
363 && panel.indicators.fullyClosed
364 && !notifications.useModal
365 && !launcher.takesFocus
367 suspended: greeter.shown
368 altTabPressed: physicalKeysMapper.altTabPressed
369 oskEnabled: shell.oskEnabled
370 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
371 panelState: panelState
373 onSpreadShownChanged: {
374 panel.indicators.hide();
375 panel.applicationMenus.hide();
382 minimumTouchPoints: 4
383 maximumTouchPoints: minimumTouchPoints
385 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
386 touchPoints.length >= minimumTouchPoints &&
387 touchPoints.length <= maximumTouchPoints
388 property bool wasPressed: false
390 onRecognisedPressChanged: {
391 if (recognisedPress) {
397 if (status !== TouchGestureArea.Recognized) {
398 if (status === TouchGestureArea.WaitingForTouch) {
399 if (wasPressed && !dragging) {
400 launcher.toggleDrawer(true);
411 objectName: "inputMethod"
414 topMargin: panel.panelHeight
415 leftMargin: (launcher.lockedByUser && launcher.lockAllowed) ? launcher.panelWidth : 0
417 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
422 objectName: "greeterLoader"
425 if (shell.mode != "shell") {
426 if (screenWindow.primary) return integratedGreeter;
427 return secondaryGreeter;
429 return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
432 item.objectName = "greeter"
434 property bool toggleDrawerAfterUnlock: false
437 function onActiveChanged() {
441 // Show drawer in case showHome() requests it
442 if (greeterLoader.toggleDrawerAfterUnlock) {
443 launcher.toggleDrawer(false);
444 greeterLoader.toggleDrawerAfterUnlock = false;
453 id: integratedGreeter
456 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
457 hides: [launcher, panel.indicators, panel.applicationMenus]
458 tabletMode: shell.usageScenario != "phone"
459 usageMode: shell.usageScenario
460 orientation: shell.orientation
461 forcedUnlock: wizard.active || shell.mode === "full-shell"
462 background: wallpaperResolver.resolvedImage
463 backgroundSourceSize: shell.largestScreenDimension
464 hasCustomBackground: wallpaperResolver.hasCustomBackground
465 inputMethodRect: inputMethod.visibleRect
466 hasKeyboard: shell.hasKeyboard
467 allowFingerprint: !dialogs.hasActiveDialog &&
468 !notifications.topmostIsFullscreen &&
469 !panel.indicators.shown
470 panelHeight: panel.panelHeight
472 // avoid overlapping with Launcher's edge drag area
473 // FIXME: Fix TouchRegistry & friends and remove this workaround
474 // Issue involves launcher's DDA getting disabled on a long
476 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
479 if (!tutorial.running) {
484 onEmergencyCall: startLockedApp("lomiri-dialer-app")
486 // Quit the greeter as soon as a session has been started
488 if (shell.mode == "greeter")
497 hides: [launcher, panel.indicators]
502 // See powerConnection for why this is useful
503 id: showGreeterDelayed
506 // Go through the dbus service, because it has checks for whether
507 // we are even allowed to lock or not.
508 DBusLomiriSessionService.PromptLock();
516 function onHasCallsChanged() {
517 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "lomiri-dialer-app") {
518 // We just received an incoming call while locked. The
519 // indicator will have already launched lomiri-dialer-app for
520 // us, but there is a race between "hasCalls" changing and the
521 // dialer starting up. So in case we lose that race, we'll
522 // start/focus the dialer ourselves here too. Even if the
523 // indicator didn't launch the dialer for some reason (or maybe
524 // a call started via some other means), if an active call is
525 // happening, we want to be in the dialer.
526 startLockedApp("lomiri-dialer-app")
535 function onStatusChanged(reason) {
536 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
537 !callManager.hasCalls && !wizard.active) {
538 // We don't want to simply call greeter.showNow() here, because
539 // that will take too long. Qt will delay button event
540 // handling until the greeter is done loading and may think the
541 // user held down the power button the whole time, leading to a
542 // power dialog being shown. Instead, delay showing the
543 // greeter until we've finished handling the event. We could
544 // make the greeter load asynchronously instead, but that
545 // introduces a whole host of timing issues, especially with
546 // its animations. So this is simpler.
547 showGreeterDelayed.start();
552 function showHome() {
553 greeter.notifyUserRequestedApp();
555 if (shell.mode === "greeter") {
556 SessionBroadcast.requestHomeShown(AccountsService.user);
558 if (!greeter.active) {
559 launcher.toggleDrawer(false);
561 greeterLoader.toggleDrawerAfterUnlock = true;
573 objectName: "fullscreenSwipeDown"
574 enabled: panel.state === "offscreen"
575 direction: SwipeArea.Downwards
576 immediateRecognition: false
585 panel.temporarilyShow()
593 anchors.fill: parent //because this draws indicator menus
594 blurSource: settings.enableBlur ? (greeter.shown ? greeter : stages) : null
595 lightMode: shell.lightMode
597 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
598 minimizedPanelHeight: units.gu(3)
599 expandedPanelHeight: units.gu(7)
600 applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
604 available: tutorial.panelEnabled
605 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
606 && (!greeter || !greeter.hasLockedApp)
607 && !shell.waitingOnGreeter
608 && settings.enableIndicatorMenu
610 model: Indicators.IndicatorsModel {
612 // tablet and phone both use the same profile
613 // FIXME: use just "phone" for greeter too, but first fix
614 // greeter app launching to either load the app inside the
615 // greeter or tell the session to load the app. This will
616 // involve taking the url-dispatcher dbus name and using
617 // SessionBroadcast to tell the session.
618 profile: shell.mode === "greeter" ? "desktop_greeter" : "phone"
619 Component.onCompleted: {
627 available: (!greeter || !greeter.shown)
628 && !shell.waitingOnGreeter
629 && !stage.spreadShown
632 readonly property bool focusedSurfaceIsFullscreen: shell.topLevelSurfaceList.focusedWindow
633 ? shell.topLevelSurfaceList.focusedWindow.state == Mir.FullscreenState
635 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
636 || greeter.hasLockedApp
637 greeterShown: greeter && greeter.shown
638 hasKeyboard: shell.hasKeyboard
639 panelState: panelState
640 supportsMultiColorLed: shell.supportsMultiColorLed
645 objectName: "launcher"
647 anchors.top: parent.top
648 anchors.topMargin: inverted ? 0 : panel.panelHeight
649 anchors.bottom: parent.bottom
651 dragAreaWidth: shell.edgeSize
652 available: tutorial.launcherEnabled
653 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
654 && !greeter.hasLockedApp
655 && !shell.waitingOnGreeter
656 && shell.mode !== "greeter"
657 visible: shell.mode !== "greeter"
658 inverted: shell.usageScenario !== "desktop"
659 superPressed: physicalKeysMapper.superPressed
660 superTabPressed: physicalKeysMapper.superTabPressed
661 panelWidth: units.gu(settings.launcherWidth)
662 lockedVisible: (lockedByUser || shell.atDesktop) && lockAllowed
663 blurSource: settings.enableBlur ? (greeter.shown ? greeter : stages) : null
664 topPanelHeight: panel.panelHeight
665 lightMode: shell.lightMode
666 drawerEnabled: !greeter.active && tutorial.launcherLongSwipeEnabled
667 privateMode: greeter.active
668 background: wallpaperResolver.resolvedImage
670 // It can be assumed that the Launcher and Panel would overlap if
671 // the Panel is open and taking up the full width of the shell
672 readonly property bool collidingWithPanel: panel && (!panel.fullyClosed && !panel.partialWidth)
674 // The "autohideLauncher" setting is only valid in desktop mode
675 readonly property bool lockedByUser: (shell.usageScenario == "desktop" && !settings.autohideLauncher)
677 // The Launcher should absolutely not be locked visible under some
679 readonly property bool lockAllowed: !collidingWithPanel && !panel.fullscreenMode && !wizard.active && !tutorial.demonstrateLauncher
681 onShowDashHome: showHome()
682 onLauncherApplicationSelected: {
683 greeter.notifyUserRequestedApp();
684 shell.activateApplication(appId);
688 panel.indicators.hide();
689 panel.applicationMenus.hide();
692 onDrawerShownChanged: {
694 panel.indicators.hide();
695 panel.applicationMenus.hide();
705 shortcut: Qt.MetaModifier | Qt.Key_A
707 launcher.toggleDrawer(true);
711 shortcut: Qt.AltModifier | Qt.Key_F1
713 launcher.openForKeyboardNavigation();
717 shortcut: Qt.MetaModifier | Qt.Key_0
719 if (LauncherModel.get(9)) {
720 activateApplication(LauncherModel.get(9).appId);
727 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
729 if (LauncherModel.get(index)) {
730 activateApplication(LauncherModel.get(index).appId);
737 KeyboardShortcutsOverlay {
738 objectName: "shortcutsOverlay"
739 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
740 && height < parent.height - padding - panel.panelHeight
741 anchors.centerIn: parent
742 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
743 anchors.verticalCenterOffset: panel.panelHeight/2
745 opacity: enabled ? 0.95 : 0
747 Behavior on opacity {
748 LomiriNumberAnimation {}
754 objectName: "tutorial"
757 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
758 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
759 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
760 inputMethod.visible ||
761 (launcher.shown && !launcher.lockedVisible) ||
762 panel.indicators.shown || stage.rightEdgeDragProgress > 0
763 usageScenario: shell.usageScenario
764 lastInputTimestamp: inputFilter.lastInputTimestamp
774 deferred: shell.mode === "greeter"
776 function unlockWhenDoneWithWizard() {
777 if (!active && shell.mode !== "greeter") {
778 ModemConnectivity.unlockAllModems();
782 Component.onCompleted: unlockWhenDoneWithWizard()
783 onActiveChanged: unlockWhenDoneWithWizard()
786 MouseArea { // modal notifications prevent interacting with other contents
788 visible: notifications.useModal
795 model: NotificationBackend.Model
797 hasMouse: shell.hasMouse
798 background: wallpaperResolver.resolvedImage
799 privacyMode: greeter.locked && AccountsService.hideNotificationContentWhileLocked
801 y: topmostIsFullscreen ? 0 : panel.panelHeight
802 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
807 when: overlay.width <= units.gu(60)
809 target: notifications
810 anchors.left: parent.left
811 anchors.right: parent.right
816 when: overlay.width > units.gu(60)
818 target: notifications
819 anchors.left: undefined
820 anchors.right: parent.right
822 PropertyChanges { target: notifications; width: units.gu(38) }
829 enabled: !greeter.shown
831 // NB: it does its own positioning according to the specified edge
835 panel.indicators.hide()
838 material: Component {
844 anchors.centerIn: parent
846 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
847 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
857 objectName: "dialogs"
859 visible: hasActiveDialog
861 usageScenario: shell.usageScenario
862 hasKeyboard: shell.hasKeyboard
864 shutdownFadeOutRectangle.enabled = true;
865 shutdownFadeOutRectangle.visible = true;
866 shutdownFadeOut.start();
871 target: SessionBroadcast
872 function onShowHome() { if (shell.mode !== "greeter") showHome() }
877 objectName: "urlDispatcher"
878 active: shell.mode === "greeter"
879 onUrlRequested: shell.activateURL(url)
886 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
889 ignoreUnknownSignals: true
890 function onItemSnapshotRequested(item) { itemGrabber.capture(item) }
895 id: cursorHidingTimer
897 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
898 onTriggered: cursor.opacity = 0;
906 topBoundaryOffset: panel.panelHeight
907 enabled: shell.hasMouse && screenWindow.active
910 property bool mouseNeverMoved: true
912 target: cursor; property: "x"; value: shell.width / 2
913 restoreMode: Binding.RestoreBinding
914 when: cursor.mouseNeverMoved && cursor.visible
917 target: cursor; property: "y"; value: shell.height / 2
918 restoreMode: Binding.RestoreBinding
919 when: cursor.mouseNeverMoved && cursor.visible
922 confiningItem: stage.itemConfiningMouseCursor
926 readonly property var previewRectangle: stage.previewRectangle.target &&
927 stage.previewRectangle.target.dragging ?
928 stage.previewRectangle : null
930 onPushedLeftBoundary: {
931 if (buttons === Qt.NoButton) {
932 launcher.pushEdge(amount);
933 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
934 previewRectangle.maximizeLeft(amount);
938 onPushedRightBoundary: {
939 if (buttons === Qt.NoButton) {
940 rightEdgeBarrier.push(amount);
941 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
942 previewRectangle.maximizeRight(amount);
946 onPushedTopBoundary: {
947 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
948 previewRectangle.maximize(amount);
951 onPushedTopLeftCorner: {
952 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
953 previewRectangle.maximizeTopLeft(amount);
956 onPushedTopRightCorner: {
957 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
958 previewRectangle.maximizeTopRight(amount);
961 onPushedBottomLeftCorner: {
962 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
963 previewRectangle.maximizeBottomLeft(amount);
966 onPushedBottomRightCorner: {
967 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
968 previewRectangle.maximizeBottomRight(amount);
972 if (previewRectangle) {
973 previewRectangle.stop();
978 mouseNeverMoved = false;
982 Behavior on opacity { LomiriNumberAnimation {} }
985 // non-visual objects
987 focusedSurface: shell.topLevelSurfaceList.focusedWindow ? shell.topLevelSurfaceList.focusedWindow.surface : null
992 id: shutdownFadeOutRectangle
999 NumberAnimation on opacity {
1004 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
1005 DBusLomiriSessionService.shutdown();