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();
383 minimumTouchPoints: 4
384 maximumTouchPoints: minimumTouchPoints
386 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
387 touchPoints.length >= minimumTouchPoints &&
388 touchPoints.length <= maximumTouchPoints
389 property bool wasPressed: false
391 onRecognisedPressChanged: {
392 if (recognisedPress) {
398 if (status !== TouchGestureArea.Recognized) {
399 if (status === TouchGestureArea.WaitingForTouch) {
400 if (wasPressed && !dragging) {
401 launcher.toggleDrawer(true);
412 objectName: "inputMethod"
415 topMargin: panel.panelHeight
416 leftMargin: (launcher.lockedByUser && launcher.lockAllowed) ? launcher.panelWidth : 0
418 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
423 objectName: "greeterLoader"
426 if (shell.mode != "shell") {
427 if (screenWindow.primary) return integratedGreeter;
428 return secondaryGreeter;
430 return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
433 item.objectName = "greeter"
435 property bool toggleDrawerAfterUnlock: false
438 function onActiveChanged() {
442 // Show drawer in case showHome() requests it
443 if (greeterLoader.toggleDrawerAfterUnlock) {
444 launcher.toggleDrawer(false);
445 greeterLoader.toggleDrawerAfterUnlock = false;
454 id: integratedGreeter
457 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
458 hides: [launcher, panel.indicators, panel.applicationMenus]
459 tabletMode: shell.usageScenario != "phone"
460 usageMode: shell.usageScenario
461 orientation: shell.orientation
462 forcedUnlock: wizard.active || shell.mode === "full-shell"
463 background: wallpaperResolver.resolvedImage
464 backgroundSourceSize: shell.largestScreenDimension
465 hasCustomBackground: wallpaperResolver.hasCustomBackground
466 inputMethodRect: inputMethod.visibleRect
467 hasKeyboard: shell.hasKeyboard
468 allowFingerprint: !dialogs.hasActiveDialog &&
469 !notifications.topmostIsFullscreen &&
470 !panel.indicators.shown
471 panelHeight: panel.panelHeight
473 // avoid overlapping with Launcher's edge drag area
474 // FIXME: Fix TouchRegistry & friends and remove this workaround
475 // Issue involves launcher's DDA getting disabled on a long
477 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
480 if (!tutorial.running) {
485 onEmergencyCall: startLockedApp("lomiri-dialer-app")
487 // Quit the greeter as soon as a session has been started
489 if (shell.mode == "greeter")
498 hides: [launcher, panel.indicators]
503 // See powerConnection for why this is useful
504 id: showGreeterDelayed
507 // Go through the dbus service, because it has checks for whether
508 // we are even allowed to lock or not.
509 DBusLomiriSessionService.PromptLock();
517 function onHasCallsChanged() {
518 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "lomiri-dialer-app") {
519 // We just received an incoming call while locked. The
520 // indicator will have already launched lomiri-dialer-app for
521 // us, but there is a race between "hasCalls" changing and the
522 // dialer starting up. So in case we lose that race, we'll
523 // start/focus the dialer ourselves here too. Even if the
524 // indicator didn't launch the dialer for some reason (or maybe
525 // a call started via some other means), if an active call is
526 // happening, we want to be in the dialer.
527 startLockedApp("lomiri-dialer-app")
536 function onStatusChanged(reason) {
537 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
538 !callManager.hasCalls && !wizard.active) {
539 // We don't want to simply call greeter.showNow() here, because
540 // that will take too long. Qt will delay button event
541 // handling until the greeter is done loading and may think the
542 // user held down the power button the whole time, leading to a
543 // power dialog being shown. Instead, delay showing the
544 // greeter until we've finished handling the event. We could
545 // make the greeter load asynchronously instead, but that
546 // introduces a whole host of timing issues, especially with
547 // its animations. So this is simpler.
548 showGreeterDelayed.start();
553 function showHome() {
554 greeter.notifyUserRequestedApp();
556 if (shell.mode === "greeter") {
557 SessionBroadcast.requestHomeShown(AccountsService.user);
559 if (!greeter.active) {
560 launcher.toggleDrawer(false);
562 greeterLoader.toggleDrawerAfterUnlock = true;
574 objectName: "fullscreenSwipeDown"
575 enabled: panel.state === "offscreen"
576 direction: SwipeArea.Downwards
577 immediateRecognition: false
586 panel.temporarilyShow()
594 anchors.fill: parent //because this draws indicator menus
595 blurSource: settings.enableBlur ? (greeter.shown ? greeter : stages) : null
596 lightMode: shell.lightMode
598 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
599 minimizedPanelHeight: units.gu(3)
600 expandedPanelHeight: units.gu(7)
601 applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
605 available: tutorial.panelEnabled
606 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
607 && (!greeter || !greeter.hasLockedApp)
608 && !shell.waitingOnGreeter
609 && settings.enableIndicatorMenu
611 model: Indicators.IndicatorsModel {
613 // tablet and phone both use the same profile
614 // FIXME: use just "phone" for greeter too, but first fix
615 // greeter app launching to either load the app inside the
616 // greeter or tell the session to load the app. This will
617 // involve taking the url-dispatcher dbus name and using
618 // SessionBroadcast to tell the session.
619 // For now indicators will just hide their buttons that
620 // usually spawn lomiri-system-settings, based on the
621 // active username being 'lightdm'.
622 profile: shell.mode === "greeter"
623 ? ((shell.usageScenario === "phone" || shell.usageScenario === "tablet")
624 ? "phone_greeter" : "desktop_greeter")
626 Component.onCompleted: {
634 available: (!greeter || !greeter.shown)
635 && !shell.waitingOnGreeter
636 && !stage.spreadShown
639 readonly property bool focusedSurfaceIsFullscreen: shell.topLevelSurfaceList.focusedWindow
640 ? shell.topLevelSurfaceList.focusedWindow.state == Mir.FullscreenState
642 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
643 || greeter.hasLockedApp
644 greeterShown: greeter && greeter.shown
645 hasKeyboard: shell.hasKeyboard
646 panelState: panelState
647 supportsMultiColorLed: shell.supportsMultiColorLed
652 objectName: "launcher"
654 anchors.top: parent.top
655 anchors.topMargin: panel.panelHeight
656 anchors.bottom: parent.bottom
658 dragAreaWidth: shell.edgeSize
659 available: tutorial.launcherEnabled
660 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
661 && !greeter.hasLockedApp
662 && !shell.waitingOnGreeter
663 && shell.mode !== "greeter"
664 visible: shell.mode !== "greeter"
665 inverted: shell.usageScenario !== "desktop"
666 superPressed: physicalKeysMapper.superPressed
667 superTabPressed: physicalKeysMapper.superTabPressed
668 panelWidth: units.gu(settings.launcherWidth)
669 lockedVisible: (lockedByUser || shell.atDesktop) && lockAllowed
670 blurSource: settings.enableBlur ? (greeter.shown ? greeter : stages) : null
671 topPanelHeight: panel.panelHeight
672 lightMode: shell.lightMode
673 drawerEnabled: !greeter.active && tutorial.launcherLongSwipeEnabled
674 privateMode: greeter.active
675 background: wallpaperResolver.resolvedImage
677 // It can be assumed that the Launcher and Panel would overlap if
678 // the Panel is open and taking up the full width of the shell
679 readonly property bool collidingWithPanel: panel && (!panel.fullyClosed && !panel.partialWidth)
681 // The "autohideLauncher" setting is only valid in desktop mode
682 readonly property bool lockedByUser: (shell.usageScenario == "desktop" && !settings.autohideLauncher)
684 // The Launcher should absolutely not be locked visible under some
686 readonly property bool lockAllowed: !collidingWithPanel && !panel.fullscreenMode && !wizard.active && !tutorial.demonstrateLauncher
688 onShowDashHome: showHome()
689 onLauncherApplicationSelected: {
690 greeter.notifyUserRequestedApp();
691 shell.activateApplication(appId);
695 panel.indicators.hide();
696 panel.applicationMenus.hide();
699 onDrawerShownChanged: {
701 panel.indicators.hide();
702 panel.applicationMenus.hide();
712 shortcut: Qt.MetaModifier | Qt.Key_A
714 launcher.toggleDrawer(true);
718 shortcut: Qt.AltModifier | Qt.Key_F1
720 launcher.openForKeyboardNavigation();
724 shortcut: Qt.MetaModifier | Qt.Key_0
726 if (LauncherModel.get(9)) {
727 activateApplication(LauncherModel.get(9).appId);
734 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
736 if (LauncherModel.get(index)) {
737 activateApplication(LauncherModel.get(index).appId);
744 KeyboardShortcutsOverlay {
745 objectName: "shortcutsOverlay"
746 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
747 && height < parent.height - padding - panel.panelHeight
748 anchors.centerIn: parent
749 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
750 anchors.verticalCenterOffset: panel.panelHeight/2
752 opacity: enabled ? 0.95 : 0
754 Behavior on opacity {
755 LomiriNumberAnimation {}
761 objectName: "tutorial"
764 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
765 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
766 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
767 inputMethod.visible ||
768 (launcher.shown && !launcher.lockedVisible) ||
769 panel.indicators.shown || stage.rightEdgeDragProgress > 0
770 usageScenario: shell.usageScenario
771 lastInputTimestamp: inputFilter.lastInputTimestamp
781 deferred: shell.mode === "greeter"
783 function unlockWhenDoneWithWizard() {
784 if (!active && shell.mode !== "greeter") {
785 ModemConnectivity.unlockAllModems();
789 Component.onCompleted: unlockWhenDoneWithWizard()
790 onActiveChanged: unlockWhenDoneWithWizard()
793 MouseArea { // modal notifications prevent interacting with other contents
795 visible: notifications.useModal
802 model: NotificationBackend.Model
804 hasMouse: shell.hasMouse
805 background: wallpaperResolver.resolvedImage
806 privacyMode: greeter.locked && AccountsService.hideNotificationContentWhileLocked
808 y: topmostIsFullscreen ? 0 : panel.panelHeight
809 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
814 when: overlay.width <= units.gu(60)
816 target: notifications
817 anchors.left: parent.left
818 anchors.right: parent.right
823 when: overlay.width > units.gu(60)
825 target: notifications
826 anchors.left: undefined
827 anchors.right: parent.right
829 PropertyChanges { target: notifications; width: units.gu(38) }
836 enabled: !greeter.shown
838 // NB: it does its own positioning according to the specified edge
842 panel.indicators.hide()
845 material: Component {
851 anchors.centerIn: parent
853 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
854 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
864 objectName: "dialogs"
866 visible: hasActiveDialog
868 usageScenario: shell.usageScenario
869 hasKeyboard: shell.hasKeyboard
871 shutdownFadeOutRectangle.enabled = true;
872 shutdownFadeOutRectangle.visible = true;
873 shutdownFadeOut.start();
878 target: SessionBroadcast
879 function onShowHome() { if (shell.mode !== "greeter") showHome() }
884 objectName: "urlDispatcher"
885 active: shell.mode === "greeter"
886 onUrlRequested: shell.activateURL(url)
893 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
896 ignoreUnknownSignals: true
897 function onItemSnapshotRequested(item) { itemGrabber.capture(item) }
902 id: cursorHidingTimer
904 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
905 onTriggered: cursor.opacity = 0;
913 topBoundaryOffset: panel.panelHeight
914 enabled: shell.hasMouse && screenWindow.active
917 property bool mouseNeverMoved: true
919 target: cursor; property: "x"; value: shell.width / 2
920 restoreMode: Binding.RestoreBinding
921 when: cursor.mouseNeverMoved && cursor.visible
924 target: cursor; property: "y"; value: shell.height / 2
925 restoreMode: Binding.RestoreBinding
926 when: cursor.mouseNeverMoved && cursor.visible
929 confiningItem: stage.itemConfiningMouseCursor
933 readonly property var previewRectangle: stage.previewRectangle.target &&
934 stage.previewRectangle.target.dragging ?
935 stage.previewRectangle : null
937 onPushedLeftBoundary: {
938 if (buttons === Qt.NoButton) {
939 launcher.pushEdge(amount);
940 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
941 previewRectangle.maximizeLeft(amount);
945 onPushedRightBoundary: {
946 if (buttons === Qt.NoButton) {
947 rightEdgeBarrier.push(amount);
948 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
949 previewRectangle.maximizeRight(amount);
953 onPushedTopBoundary: {
954 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
955 previewRectangle.maximize(amount);
958 onPushedTopLeftCorner: {
959 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
960 previewRectangle.maximizeTopLeft(amount);
963 onPushedTopRightCorner: {
964 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
965 previewRectangle.maximizeTopRight(amount);
968 onPushedBottomLeftCorner: {
969 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
970 previewRectangle.maximizeBottomLeft(amount);
973 onPushedBottomRightCorner: {
974 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
975 previewRectangle.maximizeBottomRight(amount);
979 if (previewRectangle) {
980 previewRectangle.stop();
985 mouseNeverMoved = false;
989 Behavior on opacity { LomiriNumberAnimation {} }
992 // non-visual objects
994 focusedSurface: shell.topLevelSurfaceList.focusedWindow ? shell.topLevelSurfaceList.focusedWindow.surface : null
999 id: shutdownFadeOutRectangle
1004 anchors.fill: parent
1006 NumberAnimation on opacity {
1011 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
1012 DBusLomiriSessionService.shutdown();