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();
275 id: physicalKeysMapper
276 objectName: "physicalKeysMapper"
278 onPowerKeyLongPressed: dialogs.showPowerDialog();
279 onVolumeDownTriggered: volumeControl.volumeDown();
280 onVolumeUpTriggered: volumeControl.volumeUp();
281 onScreenshotTriggered: itemGrabber.capture(shell);
285 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowInputFilter
290 Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
291 Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
295 objectName: "windowInputMonitor"
296 onHomeKeyActivated: {
297 // Ignore when greeter is active, to avoid pocket presses
298 if (!greeter.active) {
299 launcher.toggleDrawer(/* focusInputField */ false,
300 /* onlyOpen */ false,
301 /* alsoToggleLauncher */ true);
304 onTouchBegun: { cursor.opacity = 0; }
306 // move the (hidden) cursor to the last known touch position
307 var mappedCoords = mapFromItem(null, pos.x, pos.y);
308 cursor.x = mappedCoords.x;
309 cursor.y = mappedCoords.y;
310 cursor.mouseNeverMoved = false;
314 AvailableDesktopArea {
315 id: availableDesktopAreaItem
317 anchors.topMargin: panel.fullscreenMode ? 0 : panel.minimizedPanelHeight
318 anchors.leftMargin: (launcher.lockedByUser && launcher.lockAllowed) ? launcher.panelWidth : 0
323 schema.id: "com.lomiri.Shell"
328 objectName: "panelState"
335 height: parent.height
342 lightMode: shell.lightMode
344 dragAreaWidth: shell.edgeSize
345 background: wallpaperResolver.resolvedImage
346 backgroundSourceSize: shell.largestScreenDimension
348 applicationManager: ApplicationManager
349 topLevelSurfaceList: shell.topLevelSurfaceList
350 inputMethodRect: inputMethod.visibleRect
351 rightEdgePushProgress: rightEdgeBarrier.progress
352 availableDesktopArea: availableDesktopAreaItem
353 launcherLeftMargin: launcher.visibleWidth
355 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
357 : shell.usageScenario
359 mode: usageScenario == "phone" ? "staged"
360 : usageScenario == "tablet" ? "stagedWithSideStage"
363 shellOrientation: shell.orientation
364 shellOrientationAngle: shell.orientationAngle
365 orientations: shell.orientations
366 nativeWidth: shell.nativeWidth
367 nativeHeight: shell.nativeHeight
369 allowInteractivity: (!greeter || !greeter.shown)
370 && panel.indicators.fullyClosed
371 && !notifications.useModal
372 && !launcher.takesFocus
374 suspended: greeter.shown
375 altTabPressed: physicalKeysMapper.altTabPressed
376 oskEnabled: shell.oskEnabled
377 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
378 panelState: panelState
380 onSpreadShownChanged: {
381 panel.indicators.hide();
382 panel.applicationMenus.hide();
390 minimumTouchPoints: 4
391 maximumTouchPoints: minimumTouchPoints
393 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
394 touchPoints.length >= minimumTouchPoints &&
395 touchPoints.length <= maximumTouchPoints
396 property bool wasPressed: false
398 onRecognisedPressChanged: {
399 if (recognisedPress) {
405 if (status !== TouchGestureArea.Recognized) {
406 if (status === TouchGestureArea.WaitingForTouch) {
407 if (wasPressed && !dragging) {
408 launcher.toggleDrawer(true);
419 objectName: "inputMethod"
422 topMargin: panel.panelHeight
423 leftMargin: (launcher.lockedByUser && launcher.lockAllowed) ? launcher.panelWidth : 0
425 z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running || launcher.drawerShown ? overlay.z + 1 : overlay.z - 1
430 objectName: "greeterLoader"
433 if (shell.mode != "shell") {
434 if (screenWindow.primary) return integratedGreeter;
435 return secondaryGreeter;
437 return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
440 item.objectName = "greeter"
442 property bool toggleDrawerAfterUnlock: false
445 function onActiveChanged() {
449 // Show drawer in case showHome() requests it
450 if (greeterLoader.toggleDrawerAfterUnlock) {
451 launcher.toggleDrawer(false);
452 greeterLoader.toggleDrawerAfterUnlock = false;
461 id: integratedGreeter
464 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
465 hides: [launcher, panel.indicators, panel.applicationMenus]
466 tabletMode: shell.usageScenario != "phone"
467 usageMode: shell.usageScenario
468 orientation: shell.orientation
469 forcedUnlock: wizard.active || shell.mode === "full-shell"
470 background: wallpaperResolver.resolvedImage
471 backgroundSourceSize: shell.largestScreenDimension
472 hasCustomBackground: wallpaperResolver.hasCustomBackground
473 inputMethodRect: inputMethod.visibleRect
474 hasKeyboard: shell.hasKeyboard
475 allowFingerprint: !dialogs.hasActiveDialog &&
476 !notifications.topmostIsFullscreen &&
477 !panel.indicators.shown
478 panelHeight: panel.panelHeight
480 // avoid overlapping with Launcher's edge drag area
481 // FIXME: Fix TouchRegistry & friends and remove this workaround
482 // Issue involves launcher's DDA getting disabled on a long
484 dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
487 if (!tutorial.running) {
492 onEmergencyCall: startLockedApp("lomiri-dialer-app")
494 // Quit the greeter as soon as a session has been started
496 if (shell.mode == "greeter")
505 hides: [launcher, panel.indicators]
510 // See powerConnection for why this is useful
511 id: showGreeterDelayed
514 // Go through the dbus service, because it has checks for whether
515 // we are even allowed to lock or not.
516 DBusLomiriSessionService.PromptLock();
524 function onHasCallsChanged() {
525 if (greeter.locked && callManager.hasCalls && greeter.lockedApp !== "lomiri-dialer-app") {
526 // We just received an incoming call while locked. The
527 // indicator will have already launched lomiri-dialer-app for
528 // us, but there is a race between "hasCalls" changing and the
529 // dialer starting up. So in case we lose that race, we'll
530 // start/focus the dialer ourselves here too. Even if the
531 // indicator didn't launch the dialer for some reason (or maybe
532 // a call started via some other means), if an active call is
533 // happening, we want to be in the dialer.
534 startLockedApp("lomiri-dialer-app")
543 function onStatusChanged(reason) {
544 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
545 !callManager.hasCalls && !wizard.active) {
546 // We don't want to simply call greeter.showNow() here, because
547 // that will take too long. Qt will delay button event
548 // handling until the greeter is done loading and may think the
549 // user held down the power button the whole time, leading to a
550 // power dialog being shown. Instead, delay showing the
551 // greeter until we've finished handling the event. We could
552 // make the greeter load asynchronously instead, but that
553 // introduces a whole host of timing issues, especially with
554 // its animations. So this is simpler.
555 showGreeterDelayed.start();
560 function showHome() {
561 greeter.notifyUserRequestedApp();
563 if (shell.mode === "greeter") {
564 SessionBroadcast.requestHomeShown(AccountsService.user);
566 if (!greeter.active) {
567 launcher.toggleDrawer(false);
569 greeterLoader.toggleDrawerAfterUnlock = true;
581 objectName: "fullscreenSwipeDown"
582 enabled: panel.state === "offscreen"
583 direction: SwipeArea.Downwards
584 immediateRecognition: false
593 panel.temporarilyShow()
601 anchors.fill: parent //because this draws indicator menus
602 blurSource: settings.enableBlur ? (greeter.shown ? greeter : stages) : null
603 lightMode: shell.lightMode
605 mode: shell.usageScenario == "desktop" ? "windowed" : "staged"
606 minimizedPanelHeight: units.gu(3)
607 expandedPanelHeight: units.gu(7)
608 applicationMenuContentX: launcher.lockedVisible ? launcher.panelWidth : 0
612 available: tutorial.panelEnabled
613 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
614 && (!greeter || !greeter.hasLockedApp)
615 && !shell.waitingOnGreeter
616 && settings.enableIndicatorMenu
618 model: Indicators.IndicatorsModel {
620 // tablet and phone both use the same profile
621 // FIXME: use just "phone" for greeter too, but first fix
622 // greeter app launching to either load the app inside the
623 // greeter or tell the session to load the app. This will
624 // involve taking the url-dispatcher dbus name and using
625 // SessionBroadcast to tell the session.
626 // For now indicators will just hide their buttons that
627 // usually spawn lomiri-system-settings, based on the
628 // active username being 'lightdm'.
629 profile: shell.mode === "greeter"
630 ? ((shell.usageScenario === "phone" || shell.usageScenario === "tablet")
631 ? "phone_greeter" : "desktop_greeter")
633 Component.onCompleted: {
641 available: (!greeter || !greeter.shown)
642 && !shell.waitingOnGreeter
643 && !stage.spreadShown
646 readonly property bool focusedSurfaceIsFullscreen: shell.topLevelSurfaceList.focusedWindow
647 ? shell.topLevelSurfaceList.focusedWindow.state == Mir.FullscreenState
649 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
650 || greeter.hasLockedApp
651 greeterShown: greeter && greeter.shown
652 hasKeyboard: shell.hasKeyboard
653 panelState: panelState
654 supportsMultiColorLed: shell.supportsMultiColorLed
659 objectName: "launcher"
661 anchors.top: parent.top
662 anchors.topMargin: panel.panelHeight
663 anchors.bottom: parent.bottom
665 dragAreaWidth: shell.edgeSize
666 available: tutorial.launcherEnabled
667 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
668 && !greeter.hasLockedApp
669 && !shell.waitingOnGreeter
670 && shell.mode !== "greeter"
671 visible: shell.mode !== "greeter"
672 inverted: shell.usageScenario !== "desktop"
673 superPressed: physicalKeysMapper.superPressed
674 superTabPressed: physicalKeysMapper.superTabPressed
675 panelWidth: units.gu(settings.launcherWidth)
676 lockedVisible: (lockedByUser || shell.atDesktop) && lockAllowed
677 blurSource: settings.enableBlur ? (greeter.shown ? greeter : stages) : null
678 topPanelHeight: panel.panelHeight
679 lightMode: shell.lightMode
680 drawerEnabled: !greeter.active && tutorial.launcherLongSwipeEnabled
681 privateMode: greeter.active
682 background: wallpaperResolver.resolvedImage
684 // It can be assumed that the Launcher and Panel would overlap if
685 // the Panel is open and taking up the full width of the shell
686 readonly property bool collidingWithPanel: panel && (!panel.fullyClosed && !panel.partialWidth)
688 // The "autohideLauncher" setting is only valid in desktop mode
689 readonly property bool lockedByUser: (shell.usageScenario == "desktop" && !settings.autohideLauncher)
691 // The Launcher should absolutely not be locked visible under some
693 readonly property bool lockAllowed: !collidingWithPanel && !panel.fullscreenMode && !wizard.active && !tutorial.demonstrateLauncher
695 onShowDashHome: showHome()
696 onLauncherApplicationSelected: {
697 greeter.notifyUserRequestedApp();
698 shell.activateApplication(appId);
702 panel.indicators.hide();
703 panel.applicationMenus.hide();
706 onDrawerShownChanged: {
708 panel.indicators.hide();
709 panel.applicationMenus.hide();
719 shortcut: Qt.MetaModifier | Qt.Key_A
721 launcher.toggleDrawer(true);
725 shortcut: Qt.AltModifier | Qt.Key_F1
727 launcher.openForKeyboardNavigation();
731 shortcut: Qt.MetaModifier | Qt.Key_0
733 if (LauncherModel.get(9)) {
734 activateApplication(LauncherModel.get(9).appId);
741 shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
743 if (LauncherModel.get(index)) {
744 activateApplication(LauncherModel.get(index).appId);
751 KeyboardShortcutsOverlay {
752 objectName: "shortcutsOverlay"
753 enabled: launcher.shortcutHintsShown && width < parent.width - (launcher.lockedVisible ? launcher.panelWidth : 0) - padding
754 && height < parent.height - padding - panel.panelHeight
755 anchors.centerIn: parent
756 anchors.horizontalCenterOffset: launcher.lockedVisible ? launcher.panelWidth/2 : 0
757 anchors.verticalCenterOffset: panel.panelHeight/2
759 opacity: enabled ? 0.95 : 0
761 Behavior on opacity {
762 LomiriNumberAnimation {}
768 objectName: "tutorial"
771 paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
772 || !hasTouchscreen // TODO #1661557 something better for no touchscreen
773 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
774 inputMethod.visible ||
775 (launcher.shown && !launcher.lockedVisible) ||
776 panel.indicators.shown || stage.rightEdgeDragProgress > 0
777 usageScenario: shell.usageScenario
778 lastInputTimestamp: inputFilter.lastInputTimestamp
788 deferred: shell.mode === "greeter"
790 function unlockWhenDoneWithWizard() {
791 if (!active && shell.mode !== "greeter") {
792 ModemConnectivity.unlockAllModems();
796 Component.onCompleted: unlockWhenDoneWithWizard()
797 onActiveChanged: unlockWhenDoneWithWizard()
800 MouseArea { // modal notifications prevent interacting with other contents
802 visible: notifications.useModal
809 model: NotificationBackend.Model
811 hasMouse: shell.hasMouse
812 background: wallpaperResolver.resolvedImage
813 privacyMode: greeter.locked && AccountsService.hideNotificationContentWhileLocked
815 y: topmostIsFullscreen ? 0 : panel.panelHeight
816 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
821 when: overlay.width <= units.gu(60)
823 target: notifications
824 anchors.left: parent.left
825 anchors.right: parent.right
830 when: overlay.width > units.gu(60)
832 target: notifications
833 anchors.left: undefined
834 anchors.right: parent.right
836 PropertyChanges { target: notifications; width: units.gu(38) }
843 enabled: !greeter.shown
845 // NB: it does its own positioning according to the specified edge
849 panel.indicators.hide()
852 material: Component {
858 anchors.centerIn: parent
860 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
861 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
871 objectName: "dialogs"
873 visible: hasActiveDialog
875 usageScenario: shell.usageScenario
876 hasKeyboard: shell.hasKeyboard
878 shutdownFadeOutRectangle.enabled = true;
879 shutdownFadeOutRectangle.visible = true;
880 shutdownFadeOut.start();
885 target: SessionBroadcast
886 function onShowHome() { if (shell.mode !== "greeter") showHome() }
891 objectName: "urlDispatcher"
892 active: shell.mode === "greeter"
893 onUrlRequested: shell.activateURL(url)
900 GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
903 ignoreUnknownSignals: true
904 function onItemSnapshotRequested(item) { itemGrabber.capture(item) }
909 id: cursorHidingTimer
911 running: panel.focusedSurfaceIsFullscreen && cursor.opacity > 0
912 onTriggered: cursor.opacity = 0;
920 topBoundaryOffset: panel.panelHeight
921 enabled: shell.hasMouse && screenWindow.active
924 property bool mouseNeverMoved: true
926 target: cursor; property: "x"; value: shell.width / 2
927 restoreMode: Binding.RestoreBinding
928 when: cursor.mouseNeverMoved && cursor.visible
931 target: cursor; property: "y"; value: shell.height / 2
932 restoreMode: Binding.RestoreBinding
933 when: cursor.mouseNeverMoved && cursor.visible
936 confiningItem: stage.itemConfiningMouseCursor
940 readonly property var previewRectangle: stage.previewRectangle.target &&
941 stage.previewRectangle.target.dragging ?
942 stage.previewRectangle : null
944 onPushedLeftBoundary: {
945 if (buttons === Qt.NoButton) {
946 launcher.pushEdge(amount);
947 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
948 previewRectangle.maximizeLeft(amount);
952 onPushedRightBoundary: {
953 if (buttons === Qt.NoButton) {
954 rightEdgeBarrier.push(amount);
955 } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
956 previewRectangle.maximizeRight(amount);
960 onPushedTopBoundary: {
961 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
962 previewRectangle.maximize(amount);
965 onPushedTopLeftCorner: {
966 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
967 previewRectangle.maximizeTopLeft(amount);
970 onPushedTopRightCorner: {
971 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
972 previewRectangle.maximizeTopRight(amount);
975 onPushedBottomLeftCorner: {
976 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
977 previewRectangle.maximizeBottomLeft(amount);
980 onPushedBottomRightCorner: {
981 if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
982 previewRectangle.maximizeBottomRight(amount);
986 if (previewRectangle) {
987 previewRectangle.stop();
992 mouseNeverMoved = false;
996 Behavior on opacity { LomiriNumberAnimation {} }
999 // non-visual objects
1001 focusedSurface: shell.topLevelSurfaceList.focusedWindow ? shell.topLevelSurfaceList.focusedWindow.surface : null
1003 BrightnessControl {}
1006 id: shutdownFadeOutRectangle
1011 anchors.fill: parent
1013 NumberAnimation on opacity {
1018 if (shutdownFadeOutRectangle.enabled && shutdownFadeOutRectangle.visible) {
1019 DBusLomiriSessionService.shutdown();