2 * Copyright (C) 2014-2017 Canonical Ltd.
3 * Copyright (C) 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 Lomiri.Components 1.3
22import QtMir.Application 0.1
23import "../Components/PanelState"
26import Lomiri.Gestures 0.1
27import GlobalShortcut 1.0
30import "Spread/MathUtils.js" as MathUtils
31import ProcessControl 0.1
32import WindowManager 1.0
38 property QtObject applicationManager
39 property QtObject topLevelSurfaceList
40 property bool altTabPressed
41 property url background
42 property alias backgroundSourceSize: wallpaper.sourceSize
43 property int dragAreaWidth
44 property real nativeHeight
45 property real nativeWidth
46 property QtObject orientations
47 property int shellOrientation
48 property int shellOrientationAngle
49 property bool spreadEnabled: true // If false, animations and right edge will be disabled
50 property bool suspended
51 property bool oskEnabled: false
52 property bool lightMode: false
53 property rect inputMethodRect
54 property real rightEdgePushProgress: 0
55 property Item availableDesktopArea
56 property PanelState panelState
58 // Whether outside forces say that the Stage may have focus
59 property bool allowInteractivity
61 readonly property bool interactive: (state === "staged" || state === "stagedWithSideStage" || state === "windowed") && allowInteractivity
64 property string mode: "staged"
66 readonly property var temporarySelectedWorkspace: state == "spread" ? screensAndWorkspaces.activeWorkspace : null
67 property bool workspaceEnabled: (mode == "windowed" && settings.enableWorkspace) || settings.forceEnableWorkspace
69 // Used by the tutorial code
70 readonly property real rightEdgeDragProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0 // How far left the stage has been dragged
72 // used by the snap windows (edge maximize) feature
73 readonly property alias previewRectangle: fakeRectangle
75 readonly property bool spreadShown: state == "spread"
76 readonly property var mainApp: priv.focusedAppDelegate ? priv.focusedAppDelegate.application : null
78 // application windows never rotate independently
79 property int mainAppWindowOrientationAngle: shellOrientationAngle
81 property bool orientationChangesEnabled: !priv.focusedAppDelegate || priv.focusedAppDelegate.orientationChangesEnabled
83 property int supportedOrientations: {
87 return mainApp.supportedOrientations;
88 case "stagedWithSideStage":
89 var orientations = mainApp.supportedOrientations;
90 orientations |= Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation;
91 if (priv.sideStageItemId) {
92 // If we have a sidestage app, support Portrait orientation
93 // so that it will switch the sidestage app to mainstage on rotate to portrait
94 orientations |= Qt.PortraitOrientation|Qt.InvertedPortraitOrientation;
100 return Qt.PortraitOrientation |
101 Qt.LandscapeOrientation |
102 Qt.InvertedPortraitOrientation |
103 Qt.InvertedLandscapeOrientation;
108 schema.id: "com.lomiri.Shell"
111 property int launcherLeftMargin : 0
112 property bool launcherLockedVisible: false
113 property real topPanelHeight
116 target: topLevelSurfaceList
117 restoreMode: Binding.RestoreBinding
118 property: "rootFocus"
122 onInteractiveChanged: {
123 // Stage must have focus before activating windows, including null
129 onAltTabPressedChanged: {
132 if (root.spreadEnabled) {
133 altTabDelayTimer.start();
136 // Alt Tab has been released, did we already go to spread?
137 if (priv.goneToSpread) {
138 priv.goneToSpread = false;
140 // No we didn't, do a quick alt-tab
141 if (appRepeater.count > 1) {
142 appRepeater.itemAt(1).activate();
143 } else if (appRepeater.count > 0) {
144 appRepeater.itemAt(0).activate(); // quick alt-tab to the only (minimized) window should still activate it
155 if (root.altTabPressed) {
156 priv.goneToSpread = true;
161 // For MirAL window management
163 normal: Qt.rect(0, root.mode === "windowed" ? priv.windowDecorationHeight : 0, 0, 0)
167 property Item itemConfiningMouseCursor: !spreadShown && priv.focusedAppDelegate && priv.focusedAppDelegate.window && priv.focusedAppDelegate.window.confinesMousePointer ?
168 priv.focusedAppDelegate.clientAreaItem : null;
170 signal itemSnapshotRequested(Item item)
172 // functions to be called from outside
173 function updateFocusedAppOrientation() { /* TODO */ }
174 function updateFocusedAppOrientationAnimated() { /* TODO */}
176 function closeSpread() {
177 spreadItem.highlightedIndex = -1;
178 priv.goneToSpread = false;
181 onSpreadEnabledChanged: {
182 if (!spreadEnabled && spreadShown) {
187 onRightEdgePushProgressChanged: {
188 if (spreadEnabled && rightEdgePushProgress >= 1) {
189 priv.goneToSpread = true
194 id: lifecycleExceptions
195 schema.id: "com.canonical.qtmir"
198 function isExemptFromLifecycle(appId) {
199 var shortAppId = appId.split('_')[0];
200 for (var i = 0; i < lifecycleExceptions.lifecycleExemptAppids.length; i++) {
201 if (shortAppId === lifecycleExceptions.lifecycleExemptAppids[i]) {
209 id: closeFocusedShortcut
210 shortcut: Qt.AltModifier|Qt.Key_F4
212 if (priv.focusedAppDelegate) {
213 priv.focusedAppDelegate.close();
219 id: showSpreadShortcut
220 shortcut: Qt.MetaModifier|Qt.Key_W
221 active: root.spreadEnabled
222 onTriggered: priv.goneToSpread = true
226 id: minimizeAllShortcut
227 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_D
228 onTriggered: priv.minimizeAllWindows()
229 active: root.state == "windowed"
233 id: maximizeWindowShortcut
234 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up
235 onTriggered: priv.focusedAppDelegate.requestMaximize()
236 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximized
240 id: maximizeWindowLeftShortcut
241 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left
242 onTriggered: priv.focusedAppDelegate.requestMaximizeLeft()
243 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight
247 id: maximizeWindowRightShortcut
248 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right
249 onTriggered: priv.focusedAppDelegate.requestMaximizeRight()
250 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight
254 id: minimizeRestoreShortcut
255 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
257 if (priv.focusedAppDelegate.anyMaximized) {
258 priv.focusedAppDelegate.requestRestore();
260 priv.focusedAppDelegate.requestMinimize();
263 active: root.state == "windowed" && priv.focusedAppDelegate
267 shortcut: Qt.AltModifier|Qt.Key_Print
268 onTriggered: root.itemSnapshotRequested(priv.focusedAppDelegate)
269 active: priv.focusedAppDelegate !== null
273 shortcut: Qt.ControlModifier|Qt.AltModifier|Qt.Key_T
275 // try in this order: snap pkg, new deb name, old deb name
276 var candidates = ["lomiri-terminal-app_lomiri-terminal-app", "lomiri-terminal-app", "com.lomiri.terminal_terminal"];
277 for (var i = 0; i < candidates.length; i++) {
278 if (priv.startApp(candidates[i]))
285 id: showWorkspaceSwitcherShortcutLeft
286 shortcut: Qt.AltModifier|Qt.ControlModifier|Qt.Key_Left
287 active: !workspaceSwitcher.active && root.workspaceEnabled
290 workspaceSwitcher.showLeft()
294 id: showWorkspaceSwitcherShortcutRight
295 shortcut: Qt.AltModifier|Qt.ControlModifier|Qt.Key_Right
296 active: !workspaceSwitcher.active && root.workspaceEnabled
299 workspaceSwitcher.showRight()
303 id: showWorkspaceSwitcherShortcutUp
304 shortcut: Qt.AltModifier|Qt.ControlModifier|Qt.Key_Up
305 active: !workspaceSwitcher.active && root.workspaceEnabled
308 workspaceSwitcher.showUp()
312 id: showWorkspaceSwitcherShortcutDown
313 shortcut: Qt.AltModifier|Qt.ControlModifier|Qt.Key_Down
314 active: !workspaceSwitcher.active && root.workspaceEnabled
317 workspaceSwitcher.showDown()
322 id: moveAppShowWorkspaceSwitcherShortcutLeft
323 shortcut: Qt.AltModifier|Qt.ControlModifier|Qt.ShiftModifier|Qt.Key_Left
324 active: !workspaceSwitcher.active && root.workspaceEnabled && root.focusedAppDelegate
327 workspaceSwitcher.showLeftMoveApp(root.focusedAppDelegate.surface)
331 id: moveAppShowWorkspaceSwitcherShortcutRight
332 shortcut: Qt.AltModifier|Qt.ControlModifier|Qt.ShiftModifier|Qt.Key_Right
333 active: !workspaceSwitcher.active && root.workspaceEnabled && root.focusedAppDelegate
336 workspaceSwitcher.showRightMoveApp(root.focusedAppDelegate.surface)
340 id: moveAppShowWorkspaceSwitcherShortcutUp
341 shortcut: Qt.AltModifier|Qt.ControlModifier|Qt.ShiftModifier|Qt.Key_Up
342 active: !workspaceSwitcher.active && root.workspaceEnabled && root.focusedAppDelegate
345 workspaceSwitcher.showUpMoveApp(root.focusedAppDelegate.surface)
349 id: moveAppShowWorkspaceSwitcherShortcutDown
350 shortcut: Qt.AltModifier|Qt.ControlModifier|Qt.ShiftModifier|Qt.Key_Down
351 active: !workspaceSwitcher.active && root.workspaceEnabled && root.focusedAppDelegate
354 workspaceSwitcher.showDownMoveApp(root.focusedAppDelegate.surface)
360 objectName: "DesktopStagePrivate"
362 function startApp(appId) {
363 if (root.applicationManager.findApplication(appId)) {
364 return root.applicationManager.requestFocusApplication(appId);
366 return root.applicationManager.startApplication(appId) !== null;
370 property var focusedAppDelegate: null
371 property var foregroundMaximizedAppDelegate: null // for stuff like drop shadow and focusing maximized app by clicking panel
373 property bool goneToSpread: false
374 property int closingIndex: -1
375 property int animationDuration: LomiriAnimation.FastDuration
377 function updateForegroundMaximizedApp() {
379 for (var i = 0; i < appRepeater.count && !found; i++) {
380 var item = appRepeater.itemAt(i);
381 if (item && item.visuallyMaximized) {
382 foregroundMaximizedAppDelegate = item;
387 foregroundMaximizedAppDelegate = null;
391 function minimizeAllWindows() {
392 for (var i = appRepeater.count - 1; i >= 0; i--) {
393 var appDelegate = appRepeater.itemAt(i);
394 if (appDelegate && !appDelegate.minimized) {
395 appDelegate.requestMinimize();
400 readonly property bool sideStageEnabled: root.mode === "stagedWithSideStage" &&
401 (root.shellOrientation == Qt.LandscapeOrientation ||
402 root.shellOrientation == Qt.InvertedLandscapeOrientation)
403 onSideStageEnabledChanged: {
404 for (var i = 0; i < appRepeater.count; i++) {
405 appRepeater.itemAt(i).refreshStage();
407 priv.updateMainAndSideStageIndexes();
410 property var mainStageDelegate: null
411 property var sideStageDelegate: null
412 property int mainStageItemId: 0
413 property int sideStageItemId: 0
414 property string mainStageAppId: ""
415 property string sideStageAppId: ""
417 onSideStageDelegateChanged: {
418 if (!sideStageDelegate) {
423 function updateMainAndSideStageIndexes() {
424 if (root.mode != "stagedWithSideStage") {
425 priv.sideStageDelegate = null;
426 priv.sideStageItemId = 0;
427 priv.sideStageAppId = "";
428 priv.mainStageDelegate = appRepeater.itemAt(0);
429 priv.mainStageItemId = topLevelSurfaceList.idAt(0);
430 priv.mainStageAppId = topLevelSurfaceList.applicationAt(0) ? topLevelSurfaceList.applicationAt(0).appId : ""
434 var choseMainStage = false;
435 var choseSideStage = false;
437 if (!root.topLevelSurfaceList)
440 for (var i = 0; i < appRepeater.count && (!choseMainStage || !choseSideStage); ++i) {
441 var appDelegate = appRepeater.itemAt(i);
443 // This might happen during startup phase... If the delegate appears and claims focus
444 // things are updated and appRepeater.itemAt(x) still returns null while appRepeater.count >= x
445 // Lets just skip it, on startup it will be generated at a later point too...
448 if (sideStage.shown && appDelegate.stage == ApplicationInfoInterface.SideStage
449 && !choseSideStage) {
450 priv.sideStageDelegate = appDelegate
451 priv.sideStageItemId = root.topLevelSurfaceList.idAt(i);
452 priv.sideStageAppId = root.topLevelSurfaceList.applicationAt(i).appId;
453 choseSideStage = true;
454 } else if (!choseMainStage && appDelegate.stage == ApplicationInfoInterface.MainStage) {
455 priv.mainStageDelegate = appDelegate;
456 priv.mainStageItemId = root.topLevelSurfaceList.idAt(i);
457 priv.mainStageAppId = root.topLevelSurfaceList.applicationAt(i).appId;
458 choseMainStage = true;
461 if (!choseMainStage && priv.mainStageDelegate) {
462 priv.mainStageDelegate = null;
463 priv.mainStageItemId = 0;
464 priv.mainStageAppId = "";
466 if (!choseSideStage && priv.sideStageDelegate) {
467 priv.sideStageDelegate = null;
468 priv.sideStageItemId = 0;
469 priv.sideStageAppId = "";
473 property int nextInStack: {
474 var mainStageIndex = priv.mainStageDelegate ? priv.mainStageDelegate.itemIndex : -1;
475 var sideStageIndex = priv.sideStageDelegate ? priv.sideStageDelegate.itemIndex : -1;
476 if (sideStageIndex == -1) {
477 return topLevelSurfaceList.count > 1 ? 1 : -1;
479 if (mainStageIndex == 0 || sideStageIndex == 0) {
480 if (mainStageIndex == 1 || sideStageIndex == 1) {
481 return topLevelSurfaceList.count > 2 ? 2 : -1;
488 readonly property real virtualKeyboardHeight: root.inputMethodRect.height
490 readonly property real windowDecorationHeight: units.gu(3)
493 Component.onCompleted: priv.updateMainAndSideStageIndexes()
497 function onCloseClicked() { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
498 function onMinimizeClicked() { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestMinimize(); } }
499 function onRestoreClicked() { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestRestore(); } }
504 restoreMode: Binding.RestoreBinding
505 property: "decorationsVisible"
506 value: mode == "windowed" && priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized && !root.spreadShown
511 restoreMode: Binding.RestoreBinding
514 if (priv.focusedAppDelegate !== null) {
515 if (priv.focusedAppDelegate.maximized)
516 return priv.focusedAppDelegate.title
518 return priv.focusedAppDelegate.appName
522 when: priv.focusedAppDelegate
527 restoreMode: Binding.RestoreBinding
528 property: "focusedPersistentSurfaceId"
530 if (priv.focusedAppDelegate !== null) {
531 if (priv.focusedAppDelegate.surface) {
532 return priv.focusedAppDelegate.surface.persistentId;
537 when: priv.focusedAppDelegate
542 restoreMode: Binding.RestoreBinding
543 property: "dropShadow"
544 value: priv.focusedAppDelegate && !priv.focusedAppDelegate.maximized && priv.foregroundMaximizedAppDelegate !== null && mode == "windowed"
549 restoreMode: Binding.RestoreBinding
550 property: "closeButtonShown"
551 value: priv.focusedAppDelegate && priv.focusedAppDelegate.maximized
554 Component.onDestruction: {
555 panelState.title = "";
556 panelState.decorationsVisible = false;
557 panelState.dropShadow = false;
561 model: root.applicationManager
563 id: applicationDelegate
564 // TODO: figure out some lifecycle policy, like suspending minimized apps
565 // or something if running windowed.
566 // TODO: If the device has a dozen suspended apps because it was running
567 // in staged mode, when it switches to Windowed mode it will suddenly
568 // resume all those apps at once. We might want to avoid that.
569 property var requestedState: ApplicationInfoInterface.RequestedRunning
570 property bool temporaryAwaken: ProcessControl.awakenProcesses.indexOf(model.application.appId) >= 0
572 property var stateBinding: Binding {
573 target: model.application
574 property: "requestedState"
575 value: applicationDelegate.requestedState
576 restoreMode: Binding.RestoreBinding
579 property var lifecycleBinding: Binding {
580 target: model.application
581 property: "exemptFromLifecycle"
582 restoreMode: Binding.RestoreBinding
583 value: model.application
584 ? (!model.application.isTouchApp ||
585 isExemptFromLifecycle(model.application.appId) ||
586 applicationDelegate.temporaryAwaken)
591 property var focusRequestedConnection: Connections {
592 target: model.application
594 function onFocusRequested() {
595 // Application emits focusRequested when it has no surface (i.e. their processes died).
596 // Find the topmost window for this application and activate it, after which the app
597 // will be requested to be running.
599 for (var i = 0; i < appRepeater.count; i++) {
600 var appDelegate = appRepeater.itemAt(i);
601 if (appDelegate.application.appId === model.application.appId) {
602 appDelegate.activate();
607 console.warn("Application requested te be focused but no window for it. What should we do?");
615 name: "spread"; when: priv.goneToSpread
616 PropertyChanges { target: floatingFlickable; enabled: true }
617 PropertyChanges { target: root; focus: true }
618 PropertyChanges { target: spreadItem; focus: true }
619 PropertyChanges { target: hoverMouseArea; enabled: true }
620 PropertyChanges { target: rightEdgeDragArea; enabled: false }
621 PropertyChanges { target: cancelSpreadMouseArea; enabled: true }
622 PropertyChanges { target: noAppsRunningHint; visible: (root.topLevelSurfaceList.count < 1) }
623 PropertyChanges { target: blurLayer; visible: true; blurRadius: 32; brightness: .65; opacity: 1 }
624 PropertyChanges { target: wallpaper; visible: false }
625 PropertyChanges { target: screensAndWorkspaces.showTimer; running: true }
628 name: "stagedRightEdge"; when: root.spreadEnabled && (rightEdgeDragArea.dragging || rightEdgePushProgress > 0) && root.mode == "staged"
636 PropertyChanges { target: noAppsRunningHint; visible: (root.topLevelSurfaceList.count < 1) }
639 name: "sideStagedRightEdge"; when: root.spreadEnabled && (rightEdgeDragArea.dragging || rightEdgePushProgress > 0) && root.mode == "stagedWithSideStage"
640 extend: "stagedRightEdge"
643 opacity: priv.sideStageDelegate && priv.sideStageDelegate.x === sideStage.x ? 1 : 0
648 name: "windowedRightEdge"; when: root.spreadEnabled && (rightEdgeDragArea.dragging || rightEdgePushProgress > 0) && root.mode == "windowed"
654 opacity: MathUtils.linearAnimation(spreadItem.rightEdgeBreakPoint, 1, 0, 1, Math.max(rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0, rightEdgePushProgress))
658 name: "staged"; when: root.mode === "staged"
659 PropertyChanges { target: root; focus: true }
660 PropertyChanges { target: appContainer; focus: true }
663 name: "stagedWithSideStage"; when: root.mode === "stagedWithSideStage"
664 PropertyChanges { target: triGestureArea; enabled: priv.sideStageEnabled }
665 PropertyChanges { target: sideStage; visible: true }
666 PropertyChanges { target: root; focus: true }
667 PropertyChanges { target: appContainer; focus: true }
670 name: "windowed"; when: root.mode === "windowed"
671 PropertyChanges { target: root; focus: true }
672 PropertyChanges { target: appContainer; focus: true }
677 from: "stagedRightEdge,sideStagedRightEdge,windowedRightEdge"; to: "spread"
678 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
679 PropertyAction { target: screensAndWorkspaces; property: "activeWorkspace"; value: WMScreen.currentWorkspace }
680 PropertyAnimation { target: blurLayer; properties: "brightness,blurRadius"; duration: priv.animationDuration }
684 PropertyAction { target: screensAndWorkspaces; property: "activeWorkspace"; value: WMScreen.currentWorkspace }
685 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: appRepeater.count > 1 ? 1 : 0 }
686 PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
690 SequentialAnimation {
693 var item = appRepeater.itemAt(Math.max(0, spreadItem.highlightedIndex));
695 if (item.stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
698 item.playFocusAnimation();
702 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
703 PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
707 to: "stagedRightEdge,sideStagedRightEdge"
708 PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
711 to: "stagedWithSideStage"
712 ScriptAction { script: priv.updateMainAndSideStageIndexes(); }
718 id: cancelSpreadMouseArea
721 onClicked: priv.goneToSpread = false
726 objectName: "appContainer"
732 objectName: "stageBackground"
734 source: root.background
735 // Make sure it's the lowest item. Due to the left edge drag we sometimes need
736 // to put the dash at -1 and we don't want it behind the Wallpaper
747 ScreensAndWorkspaces {
748 id: screensAndWorkspaces
749 anchors { left: parent.left; top: parent.top; right: parent.right; leftMargin: root.launcherLeftMargin }
750 height: Math.max(units.gu(30), parent.height * .3)
751 background: root.background
753 enabled: workspaceEnabled
755 launcherLockedVisible: root.launcherLockedVisible
756 topPanelHeight: root.topPanelHeight
757 onCloseSpread: priv.goneToSpread = false;
758 // Clicking a workspace should put it front and center
759 onActiveWorkspaceChanged: activeWorkspace.activate()
760 opacity: visible ? 1.0 : 0.0
761 Behavior on opacity {
762 NumberAnimation { duration: priv.animationDuration }
765 property bool showAllowed : false
766 property var showTimer: Timer {
769 interval: priv.animationDuration
771 if (!priv.goneToSpread)
773 screensAndWorkspaces.showAllowed = root.workspaceEnabled;
778 onGoneToSpreadChanged: if (!priv.goneToSpread) screensAndWorkspaces.showAllowed = false
784 objectName: "spreadItem"
787 bottom: parent.bottom;
789 top: workspaceEnabled ? screensAndWorkspaces.bottom : parent.top;
791 leftMargin: root.availableDesktopArea.x
792 model: root.topLevelSurfaceList
793 spreadFlickable: floatingFlickable
794 z: root.topLevelSurfaceList.count
797 priv.goneToSpread = false;
801 appRepeater.itemAt(highlightedIndex).close();
805 id: floatingFlickable
806 objectName: "spreadFlickable"
809 contentWidth: spreadItem.spreadTotalWidth
811 function snap(toIndex) {
812 var delegate = appRepeater.itemAt(toIndex)
813 var targetContentX = floatingFlickable.contentWidth / spreadItem.totalItemCount * toIndex;
814 if (targetContentX - floatingFlickable.contentX > spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) {
815 var offset = (spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) - (targetContentX - floatingFlickable.contentX)
816 snapAnimation.to = floatingFlickable.contentX - offset;
817 snapAnimation.start();
818 } else if (targetContentX - floatingFlickable.contentX < spreadItem.leftStackXPos + units.gu(1)) {
819 var offset = (spreadItem.leftStackXPos + units.gu(1)) - (targetContentX - floatingFlickable.contentX);
820 snapAnimation.to = floatingFlickable.contentX - offset;
821 snapAnimation.start();
824 LomiriNumberAnimation {id: snapAnimation; target: floatingFlickable; property: "contentX"}
829 objectName: "hoverMouseArea"
831 propagateComposedEvents: true
835 property bool wasTouchPress: false
837 property int scrollAreaWidth: width / 3
838 property bool progressiveScrollingEnabled: false
841 mouse.accepted = false
843 if (hoverMouseArea.pressed || wasTouchPress) {
847 // Find the hovered item and mark it active
848 for (var i = appRepeater.count - 1; i >= 0; i--) {
849 var appDelegate = appRepeater.itemAt(i);
850 var mapped = mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
851 var itemUnder = appDelegate.childAt(mapped.x, mapped.y);
852 if (itemUnder && (itemUnder.objectName === "dragArea" || itemUnder.objectName === "windowInfoItem" || itemUnder.objectName == "closeMouseArea")) {
853 spreadItem.highlightedIndex = i;
858 if (floatingFlickable.contentWidth > floatingFlickable.width) {
859 var margins = floatingFlickable.width * 0.05;
861 if (!progressiveScrollingEnabled && mouseX < floatingFlickable.width - scrollAreaWidth) {
862 progressiveScrollingEnabled = true
865 // do we need to scroll?
866 if (mouseX < scrollAreaWidth + margins) {
867 var progress = Math.min(1, (scrollAreaWidth + margins - mouseX) / (scrollAreaWidth - margins));
868 var contentX = (1 - progress) * (floatingFlickable.contentWidth - floatingFlickable.width)
869 floatingFlickable.contentX = Math.max(0, Math.min(floatingFlickable.contentX, contentX))
871 if (mouseX > floatingFlickable.width - scrollAreaWidth && progressiveScrollingEnabled) {
872 var progress = Math.min(1, (mouseX - (floatingFlickable.width - scrollAreaWidth)) / (scrollAreaWidth - margins))
873 var contentX = progress * (floatingFlickable.contentWidth - floatingFlickable.width)
874 floatingFlickable.contentX = Math.min(floatingFlickable.contentWidth - floatingFlickable.width, Math.max(floatingFlickable.contentX, contentX))
880 mouse.accepted = false;
881 wasTouchPress = mouse.source === Qt.MouseEventSynthesizedByQt;
884 onExited: wasTouchPress = false;
889 id: noAppsRunningHint
891 anchors.horizontalCenter: parent.horizontalCenter
892 anchors.verticalCenter: parent.verticalCenter
894 horizontalAlignment: Qt.AlignHCenter
895 verticalAlignment: Qt.AlignVCenter
896 anchors.leftMargin: root.launcherLeftMargin
897 wrapMode: Label.WordWrap
899 text: i18n.tr("No running apps")
904 target: root.topLevelSurfaceList
905 function onListChanged() { priv.updateMainAndSideStageIndexes() }
910 objectName: "MainStageDropArea"
914 bottom: parent.bottom
916 width: appContainer.width - sideStage.width
917 enabled: priv.sideStageEnabled
920 drop.source.appDelegate.saveStage(ApplicationInfoInterface.MainStage);
921 drop.source.appDelegate.activate();
928 objectName: "sideStage"
930 height: appContainer.height
931 x: appContainer.width - width
933 showHint: !priv.sideStageDelegate
934 Behavior on opacity { LomiriNumberAnimation {} }
936 if (!priv.mainStageItemId) return 0;
938 if (priv.sideStageItemId && priv.nextInStack > 0) {
940 // Due the order in which bindings are evaluated, this might be triggered while shuffling
941 // the list and index doesn't yet match with itemIndex (even though itemIndex: index)
942 // Let's walk the list and compare itemIndex to make sure we have the correct one.
943 var nextDelegateInStack = -1;
944 for (var i = 0; i < appRepeater.count; i++) {
945 if (appRepeater.itemAt(i).itemIndex == priv.nextInStack) {
946 nextDelegateInStack = appRepeater.itemAt(i);
951 if (nextDelegateInStack.stage === ApplicationInfoInterface.MainStage) {
952 // if the next app in stack is a main stage app, put the sidestage on top of it.
962 if (!shown && priv.mainStageDelegate && !root.spreadShown) {
963 priv.mainStageDelegate.activate();
968 id: sideStageDropArea
969 objectName: "SideStageDropArea"
972 property bool dropAllowed: true
975 dropAllowed = drag.keys != "Disabled";
981 if (drop.keys == "MainStage") {
982 drop.source.appDelegate.saveStage(ApplicationInfoInterface.SideStage);
983 drop.source.appDelegate.activate();
988 if (!sideStageDropArea.drag.source) {
998 property real previewScale: .5
999 height: (screensAndWorkspaces.height - units.gu(8)) / 2
1001 width: implicitWidth * height / implicitHeight
1004 opacity: surface != null ? 1 : 0
1005 Behavior on opacity { LomiriNumberAnimation {} }
1006 visible: opacity > 0
1007 enabled: workspaceSwitcher
1010 Drag.active: surface != null
1011 Drag.keys: ["application"]
1018 model: topLevelSurfaceList
1019 objectName: "appRepeater"
1021 function indexOf(delegateItem) {
1022 for (var i = 0; i < count; i++) {
1023 if (itemAt(i) === delegateItem) {
1030 delegate: FocusScope {
1032 objectName: "appDelegate_" + model.window.id
1033 property int itemIndex: index // We need this from outside the repeater
1034 // z might be overriden in some cases by effects, but we need z ordering
1035 // to calculate occlusion detection
1036 property int normalZ: topLevelSurfaceList.count - index
1038 if (visuallyMaximized) {
1039 priv.updateForegroundMaximizedApp();
1044 opacity: fakeDragItem.surface == model.window.surface && fakeDragItem.Drag.active ? 0 : 1
1045 Behavior on opacity { LomiriNumberAnimation {} }
1047 // Set these as propertyes as they wont update otherwise
1048 property real screenOffsetX: Screen.virtualX
1049 property real screenOffsetY: Screen.virtualY
1051 // Normally we want x/y where the surface thinks it is. Width/height of our delegate will
1052 // match what the actual surface size is.
1053 // Don't write to those, they will be set by states
1055 // Here we will also need to remove the screen offset from miral's results
1056 // as lomiri x,y will be relative to the current screen only
1057 // FIXME: when proper multiscreen lands
1058 x: model.window.position.x - clientAreaItem.x - screenOffsetX
1059 y: model.window.position.y - clientAreaItem.y - screenOffsetY
1060 width: decoratedWindow.implicitWidth
1061 height: decoratedWindow.implicitHeight
1063 // requestedX/Y/width/height is what we ask the actual surface to be.
1064 // Do not write to those, they will be set by states
1065 property real requestedX: windowedX
1066 property real requestedY: windowedY
1067 property real requestedWidth: windowedWidth
1068 property real requestedHeight: windowedHeight
1070 // For both windowed and staged need to tell miral what screen we are on,
1071 // so we need to add the screen offset to the position we tell miral
1072 // FIXME: when proper multiscreen lands
1074 target: model.window; property: "requestedPosition"
1075 // miral doesn't know about our window decorations. So we have to deduct them
1076 value: Qt.point(appDelegate.requestedX + appDelegate.clientAreaItem.x + screenOffsetX,
1077 appDelegate.requestedY + appDelegate.clientAreaItem.y + screenOffsetY)
1078 when: root.mode == "windowed"
1079 restoreMode: Binding.RestoreBinding
1082 target: model.window; property: "requestedPosition"
1083 value: Qt.point(screenOffsetX, screenOffsetY)
1084 when: root.mode != "windowed"
1085 restoreMode: Binding.RestoreBinding
1088 // In those are for windowed mode. Those values basically store the window's properties
1089 // when having a floating window. If you want to move/resize a window in normal mode, this is what you want to write to.
1090 property real windowedX
1091 property real windowedY
1092 property real windowedWidth
1093 property real windowedHeight
1095 // unlike windowedX/Y, this is the last known grab position before being pushed against edges/corners
1096 // when restoring, the window should return to these, not to the place where it was dropped near the edge
1097 property real restoredX
1098 property real restoredY
1100 // Keeps track of the window geometry while in normal or restored state
1101 // Useful when returning from some maxmized state or when saving the geometry while maximized
1102 // FIXME: find a better solution
1103 property real normalX: 0
1104 property real normalY: 0
1105 property real normalWidth: 0
1106 property real normalHeight: 0
1107 function updateNormalGeometry() {
1108 if (appDelegate.state == "normal" || appDelegate.state == "restored") {
1109 normalX = appDelegate.requestedX;
1110 normalY = appDelegate.requestedY;
1111 normalWidth = appDelegate.width;
1112 normalHeight = appDelegate.height;
1115 function updateRestoredGeometry() {
1116 if (appDelegate.state == "normal" || appDelegate.state == "restored") {
1117 // save the x/y to restore to
1118 restoredX = appDelegate.x;
1119 restoredY = appDelegate.y;
1125 function onXChanged() { appDelegate.updateNormalGeometry(); }
1126 function onYChanged() { appDelegate.updateNormalGeometry(); }
1127 function onWidthChanged() { appDelegate.updateNormalGeometry(); }
1128 function onHeightChanged() { appDelegate.updateNormalGeometry(); }
1131 // True when the Stage is focusing this app and playing its own animation.
1132 // Stays true until the app is unfocused.
1133 // If it is, we don't want to play the slide in/out transition from StageMaths.
1134 // Setting it imperatively is not great, but any declarative solution hits
1135 // race conditions, causing two animations to play for one focus event.
1136 property bool inhibitSlideAnimation: false
1141 value: appDelegate.requestedY -
1142 Math.min(appDelegate.requestedY - root.availableDesktopArea.y,
1143 Math.max(0, priv.virtualKeyboardHeight - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
1144 when: root.oskEnabled && appDelegate.focus && (appDelegate.state == "normal" || appDelegate.state == "restored")
1145 && root.inputMethodRect.height > 0
1146 restoreMode: Binding.RestoreBinding
1149 Behavior on x { id: xBehavior; enabled: priv.closingIndex >= 0; LomiriNumberAnimation { onRunningChanged: if (!running) priv.closingIndex = -1} }
1153 function onShellOrientationAngleChanged() {
1154 // at this point decoratedWindow.surfaceOrientationAngle is the old shellOrientationAngle
1155 if (appDelegate.application && appDelegate.application.rotatesWindowContents) {
1156 if (root.state == "windowed") {
1157 var angleDiff = decoratedWindow.surfaceOrientationAngle - shellOrientationAngle;
1158 angleDiff = (360 + angleDiff) % 360;
1159 if (angleDiff === 90 || angleDiff === 270) {
1160 var aux = decoratedWindow.requestedHeight;
1161 decoratedWindow.requestedHeight = decoratedWindow.requestedWidth + decoratedWindow.actualDecorationHeight;
1162 decoratedWindow.requestedWidth = aux - decoratedWindow.actualDecorationHeight;
1165 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
1167 decoratedWindow.surfaceOrientationAngle = 0;
1172 readonly property alias application: decoratedWindow.application
1173 readonly property alias minimumWidth: decoratedWindow.minimumWidth
1174 readonly property alias minimumHeight: decoratedWindow.minimumHeight
1175 readonly property alias maximumWidth: decoratedWindow.maximumWidth
1176 readonly property alias maximumHeight: decoratedWindow.maximumHeight
1177 readonly property alias widthIncrement: decoratedWindow.widthIncrement
1178 readonly property alias heightIncrement: decoratedWindow.heightIncrement
1180 readonly property bool maximized: windowState === WindowStateStorage.WindowStateMaximized
1181 readonly property bool maximizedLeft: windowState === WindowStateStorage.WindowStateMaximizedLeft
1182 readonly property bool maximizedRight: windowState === WindowStateStorage.WindowStateMaximizedRight
1183 readonly property bool maximizedHorizontally: windowState === WindowStateStorage.WindowStateMaximizedHorizontally
1184 readonly property bool maximizedVertically: windowState === WindowStateStorage.WindowStateMaximizedVertically
1185 readonly property bool maximizedTopLeft: windowState === WindowStateStorage.WindowStateMaximizedTopLeft
1186 readonly property bool maximizedTopRight: windowState === WindowStateStorage.WindowStateMaximizedTopRight
1187 readonly property bool maximizedBottomLeft: windowState === WindowStateStorage.WindowStateMaximizedBottomLeft
1188 readonly property bool maximizedBottomRight: windowState === WindowStateStorage.WindowStateMaximizedBottomRight
1189 readonly property bool anyMaximized: maximized || maximizedLeft || maximizedRight || maximizedHorizontally || maximizedVertically ||
1190 maximizedTopLeft || maximizedTopRight || maximizedBottomLeft || maximizedBottomRight
1192 readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
1193 readonly property bool fullscreen: windowState === WindowStateStorage.WindowStateFullscreen
1195 readonly property bool canBeMaximized: canBeMaximizedHorizontally && canBeMaximizedVertically
1196 readonly property bool canBeMaximizedLeftRight: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
1197 (maximumHeight == 0 || maximumHeight >= appContainer.height)
1198 readonly property bool canBeCornerMaximized: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
1199 (maximumHeight == 0 || maximumHeight >= appContainer.height/2)
1200 readonly property bool canBeMaximizedHorizontally: maximumWidth == 0 || maximumWidth >= appContainer.width
1201 readonly property bool canBeMaximizedVertically: maximumHeight == 0 || maximumHeight >= appContainer.height
1202 readonly property alias orientationChangesEnabled: decoratedWindow.orientationChangesEnabled
1204 // TODO drop our own windowType once Mir/Miral/Qtmir gets in sync with ours
1205 property int windowState: WindowStateStorage.WindowStateNormal
1206 property int prevWindowState: WindowStateStorage.WindowStateRestored
1208 property bool animationsEnabled: true
1209 property alias title: decoratedWindow.title
1210 readonly property string appName: model.application ? model.application.name : ""
1211 property bool visuallyMaximized: false
1212 property bool visuallyMinimized: false
1213 readonly property alias windowedTransitionRunning: windowedTransition.running
1215 property int stage: ApplicationInfoInterface.MainStage
1216 function saveStage(newStage) {
1217 appDelegate.stage = newStage;
1218 WindowStateStorage.saveStage(appId, newStage);
1219 priv.updateMainAndSideStageIndexes()
1222 readonly property var surface: model.window.surface
1223 readonly property var window: model.window
1225 readonly property alias focusedSurface: decoratedWindow.focusedSurface
1226 readonly property bool dragging: touchControls.overlayShown ? touchControls.dragging : decoratedWindow.dragging
1228 readonly property string appId: model.application.appId
1229 readonly property alias clientAreaItem: decoratedWindow.clientAreaItem
1231 // It is Lomiri policy to close any window but the last one during OOM teardown
1234 target: model.window.surface
1236 if ((!surface.live && application && application.surfaceCount > 1) || !application)
1237 topLevelSurfaceList.removeAt(appRepeater.indexOf(appDelegate));
1243 function activate() {
1244 if (model.window.focused) {
1245 updateQmlFocusFromMirSurfaceFocus();
1248 // Activate the window since it has a surface (with a running app) backing it
1249 model.window.activate();
1251 // Otherwise, cause a respawn of the app, and trigger it's refocusing as the last window
1252 topLevelSurfaceList.raiseId(model.window.id);
1256 function requestMaximize() { model.window.requestState(Mir.MaximizedState); }
1257 function requestMaximizeVertically() { model.window.requestState(Mir.VertMaximizedState); }
1258 function requestMaximizeHorizontally() { model.window.requestState(Mir.HorizMaximizedState); }
1259 function requestMaximizeLeft() { model.window.requestState(Mir.MaximizedLeftState); }
1260 function requestMaximizeRight() { model.window.requestState(Mir.MaximizedRightState); }
1261 function requestMaximizeTopLeft() { model.window.requestState(Mir.MaximizedTopLeftState); }
1262 function requestMaximizeTopRight() { model.window.requestState(Mir.MaximizedTopRightState); }
1263 function requestMaximizeBottomLeft() { model.window.requestState(Mir.MaximizedBottomLeftState); }
1264 function requestMaximizeBottomRight() { model.window.requestState(Mir.MaximizedBottomRightState); }
1265 function requestMinimize() { model.window.requestState(Mir.MinimizedState); }
1266 function requestRestore() { model.window.requestState(Mir.RestoredState); }
1268 function claimFocus() {
1269 if (root.state == "spread") {
1270 spreadItem.highlightedIndex = index
1271 // force pendingActivation so that when switching to staged mode, topLevelSurfaceList focus won't got to previous app ( case when apps are launched from outside )
1272 topLevelSurfaceList.pendingActivation();
1273 priv.goneToSpread = false;
1275 if (root.mode == "stagedWithSideStage") {
1276 if (appDelegate.stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
1279 priv.updateMainAndSideStageIndexes();
1281 appDelegate.focus = true;
1283 // Don't set focusedAppDelegate (and signal mainAppChanged) unnecessarily
1284 // which can happen after getting interactive again.
1285 if (priv.focusedAppDelegate !== appDelegate)
1286 priv.focusedAppDelegate = appDelegate;
1289 function updateQmlFocusFromMirSurfaceFocus() {
1290 if (model.window.focused) {
1292 decoratedWindow.focus = true;
1297 id: windowStateSaver
1299 screenWidth: appContainer.width
1300 screenHeight: appContainer.height
1301 leftMargin: root.availableDesktopArea.x
1302 minimumY: root.availableDesktopArea.y
1306 target: model.window
1307 function onFocusedChanged() {
1308 updateQmlFocusFromMirSurfaceFocus();
1309 if (!model.window.focused) {
1310 inhibitSlideAnimation = false;
1313 function onFocusRequested() {
1314 appDelegate.activate();
1316 function onStateChanged(value) {
1317 if (value == Mir.MinimizedState) {
1318 appDelegate.minimize();
1319 } else if (value == Mir.MaximizedState) {
1320 appDelegate.maximize();
1321 } else if (value == Mir.VertMaximizedState) {
1322 appDelegate.maximizeVertically();
1323 } else if (value == Mir.HorizMaximizedState) {
1324 appDelegate.maximizeHorizontally();
1325 } else if (value == Mir.MaximizedLeftState) {
1326 appDelegate.maximizeLeft();
1327 } else if (value == Mir.MaximizedRightState) {
1328 appDelegate.maximizeRight();
1329 } else if (value == Mir.MaximizedTopLeftState) {
1330 appDelegate.maximizeTopLeft();
1331 } else if (value == Mir.MaximizedTopRightState) {
1332 appDelegate.maximizeTopRight();
1333 } else if (value == Mir.MaximizedBottomLeftState) {
1334 appDelegate.maximizeBottomLeft();
1335 } else if (value == Mir.MaximizedBottomRightState) {
1336 appDelegate.maximizeBottomRight();
1337 } else if (value == Mir.RestoredState) {
1338 if (appDelegate.fullscreen && appDelegate.prevWindowState != WindowStateStorage.WindowStateRestored
1339 && appDelegate.prevWindowState != WindowStateStorage.WindowStateNormal) {
1340 model.window.requestState(WindowStateStorage.toMirState(appDelegate.prevWindowState));
1342 appDelegate.restore();
1344 } else if (value == Mir.FullscreenState) {
1345 appDelegate.prevWindowState = appDelegate.windowState;
1346 appDelegate.windowState = WindowStateStorage.WindowStateFullscreen;
1351 readonly property bool windowReady: clientAreaItem.surfaceInitialized
1352 onWindowReadyChanged: {
1354 var loadedMirState = WindowStateStorage.toMirState(windowStateSaver.loadedState);
1355 var state = loadedMirState;
1357 if (window.state == Mir.FullscreenState) {
1358 // If the app is fullscreen at startup, we should not use saved state
1359 // Example of why: if you open game that only requests fullscreen at
1360 // Statup, this will automaticly be set to "restored state" since
1361 // thats the default value of stateStorage, this will result in the app
1362 // having the "restored state" as it will not make a fullscreen
1363 // call after the app has started.
1364 console.log("Initial window state is fullscreen, not using saved state.");
1365 state = window.state;
1366 } else if (loadedMirState == Mir.FullscreenState) {
1367 // If saved state is fullscreen, we should use app initial state
1368 // Example of why: if you open browser with youtube video at fullscreen
1369 // and close this app, it will be fullscreen next time you open the app.
1370 console.log("Saved window state is fullscreen, using initial window state");
1371 state = window.state;
1374 // need to apply the shell chrome policy on top the saved window state
1376 if (root.mode == "windowed") {
1377 policy = windowedFullscreenPolicy;
1379 policy = stagedFullscreenPolicy
1381 window.requestState(policy.applyPolicy(state, surface.shellChrome));
1385 Component.onCompleted: {
1386 if (application && application.rotatesWindowContents) {
1387 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
1389 decoratedWindow.surfaceOrientationAngle = 0;
1392 // First, cascade the newly created window, relative to the currently/old focused window.
1393 windowedX = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedX + units.gu(3) : (normalZ - 1) * units.gu(3)
1394 windowedY = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedY + units.gu(3) : normalZ * units.gu(3)
1395 // Now load any saved state. This needs to happen *after* the cascading!
1396 windowStateSaver.load();
1398 updateQmlFocusFromMirSurfaceFocus();
1401 _constructing = false;
1403 Component.onDestruction: {
1404 windowStateSaver.save();
1407 // This stage is about to be destroyed. Don't mess up with the model at this point
1411 if (visuallyMaximized) {
1412 priv.updateForegroundMaximizedApp();
1416 onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
1418 property bool _constructing: true;
1420 if (!_constructing) {
1421 priv.updateMainAndSideStageIndexes();
1427 && !greeter.fullyShown
1428 && (priv.foregroundMaximizedAppDelegate === null || priv.foregroundMaximizedAppDelegate.normalZ <= z)
1430 || appDelegate.fullscreen
1431 || focusAnimation.running || rightEdgeFocusAnimation.running || hidingAnimation.running
1434 model.window.close();
1437 function maximize(animated) {
1438 animationsEnabled = (animated === undefined) || animated;
1439 windowState = WindowStateStorage.WindowStateMaximized;
1441 function maximizeLeft(animated) {
1442 animationsEnabled = (animated === undefined) || animated;
1443 windowState = WindowStateStorage.WindowStateMaximizedLeft;
1445 function maximizeRight(animated) {
1446 animationsEnabled = (animated === undefined) || animated;
1447 windowState = WindowStateStorage.WindowStateMaximizedRight;
1449 function maximizeHorizontally(animated) {
1450 animationsEnabled = (animated === undefined) || animated;
1451 windowState = WindowStateStorage.WindowStateMaximizedHorizontally;
1453 function maximizeVertically(animated) {
1454 animationsEnabled = (animated === undefined) || animated;
1455 windowState = WindowStateStorage.WindowStateMaximizedVertically;
1457 function maximizeTopLeft(animated) {
1458 animationsEnabled = (animated === undefined) || animated;
1459 windowState = WindowStateStorage.WindowStateMaximizedTopLeft;
1461 function maximizeTopRight(animated) {
1462 animationsEnabled = (animated === undefined) || animated;
1463 windowState = WindowStateStorage.WindowStateMaximizedTopRight;
1465 function maximizeBottomLeft(animated) {
1466 animationsEnabled = (animated === undefined) || animated;
1467 windowState = WindowStateStorage.WindowStateMaximizedBottomLeft;
1469 function maximizeBottomRight(animated) {
1470 animationsEnabled = (animated === undefined) || animated;
1471 windowState = WindowStateStorage.WindowStateMaximizedBottomRight;
1473 function minimize(animated) {
1474 animationsEnabled = (animated === undefined) || animated;
1475 windowState |= WindowStateStorage.WindowStateMinimized; // add the minimized bit
1477 function restore(animated,state) {
1478 animationsEnabled = (animated === undefined) || animated;
1479 windowState = state || WindowStateStorage.WindowStateRestored;
1480 windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
1481 prevWindowState = windowState;
1484 function playFocusAnimation() {
1485 if (state == "stagedRightEdge") {
1486 // TODO: Can we drop this if and find something that always works?
1487 if (root.mode == "staged") {
1488 rightEdgeFocusAnimation.targetX = 0
1489 rightEdgeFocusAnimation.start()
1490 } else if (root.mode == "stagedWithSideStage") {
1491 rightEdgeFocusAnimation.targetX = appDelegate.stage == ApplicationInfoInterface.SideStage ? sideStage.x : 0
1492 rightEdgeFocusAnimation.start()
1495 focusAnimation.start()
1498 function playHidingAnimation() {
1499 if (state != "windowedRightEdge") {
1500 hidingAnimation.start()
1504 function refreshStage() {
1505 var newStage = ApplicationInfoInterface.MainStage;
1506 if (priv.sideStageEnabled) { // we're in lanscape rotation.
1507 if (application && application.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {
1508 var defaultStage = ApplicationInfoInterface.SideStage; // if application supports portrait, it defaults to sidestage.
1509 if (application.supportedOrientations & (Qt.LandscapeOrientation|Qt.InvertedLandscapeOrientation)) {
1510 // if it supports lanscape, it defaults to mainstage.
1511 defaultStage = ApplicationInfoInterface.MainStage;
1513 newStage = WindowStateStorage.getStage(application.appId, defaultStage);
1518 if (focus && stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
1523 LomiriNumberAnimation {
1529 duration: LomiriAnimation.SnapDuration
1531 topLevelSurfaceList.pendingActivation();
1532 topLevelSurfaceList.raiseId(model.window.id);
1535 appDelegate.activate();
1539 id: rightEdgeFocusAnimation
1540 property int targetX: 0
1541 LomiriNumberAnimation { target: appDelegate; properties: "x"; to: rightEdgeFocusAnimation.targetX; duration: priv.animationDuration }
1542 LomiriNumberAnimation { target: decoratedWindow; properties: "angle"; to: 0; duration: priv.animationDuration }
1543 LomiriNumberAnimation { target: decoratedWindow; properties: "itemScale"; to: 1; duration: priv.animationDuration }
1545 topLevelSurfaceList.pendingActivation();
1546 inhibitSlideAnimation = true;
1549 appDelegate.activate();
1554 LomiriNumberAnimation { target: appDelegate; property: "opacity"; to: 0; duration: priv.animationDuration }
1555 onStopped: appDelegate.opacity = 1
1562 flickable: floatingFlickable
1566 sceneWidth: root.width
1567 stage: appDelegate.stage
1568 thisDelegate: appDelegate
1569 mainStageDelegate: priv.mainStageDelegate
1570 sideStageDelegate: priv.sideStageDelegate
1571 sideStageWidth: sideStage.panelWidth
1572 sideStageHandleWidth: sideStage.handleWidth
1573 sideStageX: sideStage.x
1574 itemIndex: appDelegate.itemIndex
1575 nextInStack: priv.nextInStack
1576 animationDuration: priv.animationDuration
1579 StagedRightEdgeMaths {
1580 id: stagedRightEdgeMaths
1581 sceneWidth: root.availableDesktopArea.width
1582 sceneHeight: appContainer.height
1583 isMainStageApp: priv.mainStageDelegate == appDelegate
1584 isSideStageApp: priv.sideStageDelegate == appDelegate
1585 sideStageWidth: sideStage.width
1586 sideStageOpen: sideStage.shown
1588 nextInStack: priv.nextInStack
1590 targetHeight: spreadItem.stackHeight
1591 targetX: spreadMaths.targetX
1592 startY: appDelegate.fullscreen ? 0 : root.availableDesktopArea.y
1593 targetY: spreadMaths.targetY
1594 targetAngle: spreadMaths.targetAngle
1595 targetScale: spreadMaths.targetScale
1596 shuffledZ: stageMaths.itemZ
1597 breakPoint: spreadItem.rightEdgeBreakPoint
1600 WindowedRightEdgeMaths {
1601 id: windowedRightEdgeMaths
1603 startWidth: appDelegate.requestedWidth
1604 startHeight: appDelegate.requestedHeight
1605 targetHeight: spreadItem.stackHeight
1606 targetX: spreadMaths.targetX
1607 targetY: spreadMaths.targetY
1608 normalZ: appDelegate.normalZ
1609 targetAngle: spreadMaths.targetAngle
1610 targetScale: spreadMaths.targetScale
1611 breakPoint: spreadItem.rightEdgeBreakPoint
1616 name: "spread"; when: root.state == "spread"
1617 StateChangeScript { script: { decoratedWindow.cancelDrag(); } }
1619 target: decoratedWindow;
1620 showDecoration: false;
1621 angle: spreadMaths.targetAngle
1622 itemScale: spreadMaths.targetScale
1623 scaleToPreviewSize: spreadItem.stackHeight
1624 scaleToPreviewProgress: 1
1625 hasDecoration: root.mode === "windowed"
1626 shadowOpacity: spreadMaths.shadowOpacity
1627 showHighlight: spreadItem.highlightedIndex === index
1628 darkening: spreadItem.highlightedIndex >= 0
1629 anchors.topMargin: dragArea.distance
1633 x: spreadMaths.targetX
1634 y: spreadMaths.targetY
1636 height: spreadItem.spreadItemHeight
1637 visible: spreadMaths.itemVisible
1639 PropertyChanges { target: dragArea; enabled: true }
1640 PropertyChanges { target: windowInfoItem; opacity: spreadMaths.tileInfoOpacity; visible: spreadMaths.itemVisible }
1641 PropertyChanges { target: touchControls; enabled: false }
1644 name: "stagedRightEdge"
1645 when: (root.mode == "staged" || root.mode == "stagedWithSideStage") && (root.state == "sideStagedRightEdge" || root.state == "stagedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running)
1647 target: stagedRightEdgeMaths
1648 progress: Math.max(rightEdgePushProgress, rightEdgeDragArea.draggedProgress)
1652 x: stagedRightEdgeMaths.animatedX
1653 y: stagedRightEdgeMaths.animatedY
1654 z: stagedRightEdgeMaths.animatedZ
1655 height: stagedRightEdgeMaths.animatedHeight
1656 visible: appDelegate.x < root.width
1659 target: decoratedWindow
1660 hasDecoration: false
1661 angle: stagedRightEdgeMaths.animatedAngle
1662 itemScale: stagedRightEdgeMaths.animatedScale
1663 scaleToPreviewSize: spreadItem.stackHeight
1664 scaleToPreviewProgress: stagedRightEdgeMaths.scaleToPreviewProgress
1667 // make sure it's visible but transparent so it fades in when we transition to spread
1668 PropertyChanges { target: windowInfoItem; opacity: 0; visible: true }
1671 name: "windowedRightEdge"
1672 when: root.mode == "windowed" && (root.state == "windowedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running || rightEdgePushProgress > 0)
1674 target: windowedRightEdgeMaths
1675 swipeProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0
1676 pushProgress: rightEdgePushProgress
1680 x: windowedRightEdgeMaths.animatedX
1681 y: windowedRightEdgeMaths.animatedY
1682 z: windowedRightEdgeMaths.animatedZ
1683 height: stagedRightEdgeMaths.animatedHeight
1686 target: decoratedWindow
1687 showDecoration: windowedRightEdgeMaths.decorationHeight
1688 angle: windowedRightEdgeMaths.animatedAngle
1689 itemScale: windowedRightEdgeMaths.animatedScale
1690 scaleToPreviewSize: spreadItem.stackHeight
1691 scaleToPreviewProgress: windowedRightEdgeMaths.scaleToPreviewProgress
1695 target: opacityEffect;
1696 opacityValue: windowedRightEdgeMaths.opacityMask
1697 sourceItem: windowedRightEdgeMaths.opacityMask < 1 ? decoratedWindow : null
1701 name: "staged"; when: root.state == "staged"
1705 y: root.availableDesktopArea.y
1706 visuallyMaximized: true
1707 visible: appDelegate.x < root.width
1711 requestedWidth: appContainer.width
1712 requestedHeight: root.availableDesktopArea.height
1713 restoreEntryValues: false
1716 target: decoratedWindow
1717 hasDecoration: false
1725 animateX: !focusAnimation.running && !rightEdgeFocusAnimation.running && itemIndex !== spreadItem.highlightedIndex && !inhibitSlideAnimation
1728 target: appDelegate.window
1729 allowClientResize: false
1733 name: "stagedWithSideStage"; when: root.state == "stagedWithSideStage"
1741 y: root.availableDesktopArea.y
1743 visuallyMaximized: true
1744 visible: appDelegate.x < root.width
1748 requestedWidth: stageMaths.itemWidth
1749 requestedHeight: root.availableDesktopArea.height
1750 restoreEntryValues: false
1753 target: decoratedWindow
1754 hasDecoration: false
1761 target: appDelegate.window
1762 allowClientResize: false
1766 name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
1768 target: appDelegate;
1769 requestedX: root.availableDesktopArea.x;
1771 visuallyMinimized: false;
1772 visuallyMaximized: true
1776 requestedWidth: root.availableDesktopArea.width;
1777 requestedHeight: appContainer.height;
1778 restoreEntryValues: false
1780 PropertyChanges { target: touchControls; enabled: true }
1781 PropertyChanges { target: decoratedWindow; windowControlButtonsVisible: false }
1784 name: "fullscreen"; when: appDelegate.fullscreen && !appDelegate.minimized
1786 target: appDelegate;
1792 requestedWidth: appContainer.width
1793 requestedHeight: appContainer.height
1794 restoreEntryValues: false
1796 PropertyChanges { target: decoratedWindow; hasDecoration: false }
1800 when: appDelegate.windowState == WindowStateStorage.WindowStateNormal
1803 visuallyMinimized: false
1805 PropertyChanges { target: touchControls; enabled: true }
1806 PropertyChanges { target: resizeArea; enabled: true }
1807 PropertyChanges { target: decoratedWindow; shadowOpacity: .3; windowControlButtonsVisible: true}
1810 requestedWidth: windowedWidth
1811 requestedHeight: windowedHeight
1812 restoreEntryValues: false
1817 when: appDelegate.windowState == WindowStateStorage.WindowStateRestored
1820 restoreEntryValues: false
1821 target: appDelegate;
1822 windowedX: restoredX;
1823 windowedY: restoredY;
1827 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
1831 windowedX: root.availableDesktopArea.x
1832 windowedY: root.availableDesktopArea.y
1833 windowedWidth: root.availableDesktopArea.width / 2
1834 windowedHeight: root.availableDesktopArea.height
1838 name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
1839 extend: "maximizedLeft"
1841 target: appDelegate;
1842 windowedX: root.availableDesktopArea.x + (root.availableDesktopArea.width / 2)
1846 name: "maximizedTopLeft"; when: appDelegate.maximizedTopLeft && !appDelegate.minimized
1850 windowedX: root.availableDesktopArea.x
1851 windowedY: root.availableDesktopArea.y
1852 windowedWidth: root.availableDesktopArea.width / 2
1853 windowedHeight: root.availableDesktopArea.height / 2
1857 name: "maximizedTopRight"; when: appDelegate.maximizedTopRight && !appDelegate.minimized
1858 extend: "maximizedTopLeft"
1861 windowedX: root.availableDesktopArea.x + (root.availableDesktopArea.width / 2)
1865 name: "maximizedBottomLeft"; when: appDelegate.maximizedBottomLeft && !appDelegate.minimized
1869 windowedX: root.availableDesktopArea.x
1870 windowedY: root.availableDesktopArea.y + (root.availableDesktopArea.height / 2)
1871 windowedWidth: root.availableDesktopArea.width / 2
1872 windowedHeight: root.availableDesktopArea.height / 2
1876 name: "maximizedBottomRight"; when: appDelegate.maximizedBottomRight && !appDelegate.minimized
1877 extend: "maximizedBottomLeft"
1880 windowedX: root.availableDesktopArea.x + (root.availableDesktopArea.width / 2)
1884 name: "maximizedHorizontally"; when: appDelegate.maximizedHorizontally && !appDelegate.minimized
1888 windowedX: root.availableDesktopArea.x; windowedY: windowedY
1889 windowedWidth: root.availableDesktopArea.width; windowedHeight: windowedHeight
1893 name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
1897 windowedX: windowedX; windowedY: root.availableDesktopArea.y
1898 windowedWidth: windowedWidth; windowedHeight: root.availableDesktopArea.height
1902 name: "minimized"; when: appDelegate.minimized
1905 scale: units.gu(5) / appDelegate.width
1907 visuallyMinimized: true
1908 visuallyMaximized: false
1909 x: -appDelegate.width / 2
1910 y: root.height / 2 //TODO
1917 // These two animate applications into position from Staged to Desktop and back
1919 from: "staged,stagedWithSideStage"
1920 to: "normal,restored,maximized,maximizedHorizontally,maximizedVertically,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedBottomLeft,maximizedTopRight,maximizedBottomRight"
1921 enabled: appDelegate.animationsEnabled
1922 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
1923 LomiriNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,opacity,requestedWidth,requestedHeight,scale"; duration: priv.animationDuration }
1926 from: "normal,restored,maximized,maximizedHorizontally,maximizedVertically,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedBottomLeft,maximizedTopRight,maximizedBottomRight"
1927 to: "staged,stagedWithSideStage"
1928 LomiriNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,requestedWidth,requestedHeight"; duration: priv.animationDuration}
1932 from: "normal,restored,maximized,maximizedHorizontally,maximizedVertically,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedBottomLeft,maximizedTopRight,maximizedBottomRight,staged,stagedWithSideStage,windowedRightEdge,stagedRightEdge";
1934 // DecoratedWindow wants the scaleToPreviewSize set before enabling scaleToPreview
1935 PropertyAction { target: appDelegate; properties: "z,visible" }
1936 PropertyAction { target: decoratedWindow; property: "scaleToPreviewSize" }
1937 LomiriNumberAnimation { target: appDelegate; properties: "x,y,height"; duration: priv.animationDuration }
1938 LomiriNumberAnimation { target: decoratedWindow; properties: "width,height,itemScale,angle,scaleToPreviewProgress"; duration: priv.animationDuration }
1939 LomiriNumberAnimation { target: windowInfoItem; properties: "opacity"; duration: priv.animationDuration }
1942 from: "normal,staged"; to: "stagedWithSideStage"
1943 LomiriNumberAnimation { target: appDelegate; properties: "x,y,requestedWidth,requestedHeight"; duration: priv.animationDuration }
1946 to: "windowedRightEdge"
1949 windowedRightEdgeMaths.startX = appDelegate.requestedX
1950 windowedRightEdgeMaths.startY = appDelegate.requestedY
1953 var thisRect = { x: appDelegate.windowedX, y: appDelegate.windowedY, width: appDelegate.requestedWidth, height: appDelegate.requestedHeight }
1954 var otherDelegate = appRepeater.itemAt(0);
1955 var otherRect = { x: otherDelegate.windowedX, y: otherDelegate.windowedY, width: otherDelegate.requestedWidth, height: otherDelegate.requestedHeight }
1956 var intersectionRect = MathUtils.intersectionRect(thisRect, otherRect)
1957 var mappedInterSectionRect = appDelegate.mapFromItem(root, intersectionRect.x, intersectionRect.y)
1958 opacityEffect.maskX = mappedInterSectionRect.x
1959 opacityEffect.maskY = mappedInterSectionRect.y
1960 opacityEffect.maskWidth = intersectionRect.width
1961 opacityEffect.maskHeight = intersectionRect.height
1967 from: "stagedRightEdge"; to: "staged"
1968 enabled: rightEdgeDragArea.cancelled // only transition back to state if the gesture was cancelled, in the other cases we play the focusAnimations.
1969 SequentialAnimation {
1971 LomiriNumberAnimation { target: appDelegate; properties: "x,y,height,width,scale"; duration: priv.animationDuration }
1972 LomiriNumberAnimation { target: decoratedWindow; properties: "width,height,itemScale,angle,scaleToPreviewProgress"; duration: priv.animationDuration }
1974 // We need to release scaleToPreviewSize at last
1975 PropertyAction { target: decoratedWindow; property: "scaleToPreviewSize" }
1976 PropertyAction { target: appDelegate; property: "visible" }
1980 from: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1982 SequentialAnimation {
1983 ScriptAction { script: { fakeRectangle.stop(); } }
1984 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1985 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1986 LomiriNumberAnimation { target: appDelegate; properties: "x,y,scale,opacity"; duration: priv.animationDuration }
1987 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1992 to: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1993 SequentialAnimation {
1994 PropertyAction { target: appDelegate; property: "visuallyMinimized,z" }
1996 LomiriNumberAnimation { target: appDelegate; properties: "x"; from: -appDelegate.width / 2; duration: priv.animationDuration }
1997 LomiriNumberAnimation { target: appDelegate; properties: "y,opacity"; duration: priv.animationDuration }
1998 LomiriNumberAnimation { target: appDelegate; properties: "scale"; from: 0; duration: priv.animationDuration }
2000 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
2004 id: windowedTransition
2005 from: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen,minimized"
2006 to: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
2007 enabled: appDelegate.animationsEnabled
2008 SequentialAnimation {
2009 ScriptAction { script: {
2010 if (appDelegate.visuallyMaximized) visuallyMaximized = false; // maximized before -> going to restored
2013 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
2014 LomiriNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,windowedX,windowedY,opacity,scale,requestedWidth,requestedHeight,windowedWidth,windowedHeight";
2015 duration: priv.animationDuration }
2016 ScriptAction { script: {
2017 fakeRectangle.stop();
2018 appDelegate.visuallyMaximized = appDelegate.maximized; // reflect the target state
2027 property: "decorationsAlwaysVisible"
2028 value: appDelegate && appDelegate.maximized && touchControls.overlayShown
2029 restoreMode: Binding.RestoreBinding
2034 objectName: "windowResizeArea"
2036 anchors.fill: appDelegate
2038 // workaround so that it chooses the correct resize borders when you drag from a corner ResizeGrip
2039 anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
2042 boundsItem: root.availableDesktopArea
2043 minWidth: units.gu(10)
2044 minHeight: units.gu(10)
2045 borderThickness: units.gu(2)
2048 readyToAssesBounds: !appDelegate._constructing
2051 appDelegate.activate();
2057 objectName: "decoratedWindow"
2058 anchors.left: appDelegate.left
2059 anchors.top: appDelegate.top
2060 application: model.application
2061 surface: model.window.surface
2062 active: model.window.focused
2064 interactive: root.interactive
2066 decorationHeight: priv.windowDecorationHeight
2067 maximizeButtonShown: appDelegate.canBeMaximized
2068 overlayShown: touchControls.overlayShown
2069 width: implicitWidth
2070 height: implicitHeight
2071 highlightSize: windowInfoItem.iconMargin / 2
2072 boundsItem: root.availableDesktopArea
2073 panelState: root.panelState
2074 altDragEnabled: root.mode == "windowed"
2075 lightMode: root.lightMode
2077 requestedWidth: appDelegate.requestedWidth
2078 requestedHeight: appDelegate.requestedHeight
2080 onCloseClicked: { appDelegate.close(); }
2081 onMaximizeClicked: {
2082 if (appDelegate.canBeMaximized) {
2083 appDelegate.anyMaximized ? appDelegate.requestRestore() : appDelegate.requestMaximize();
2086 onMaximizeHorizontallyClicked: {
2087 if (appDelegate.canBeMaximizedHorizontally) {
2088 appDelegate.maximizedHorizontally ? appDelegate.requestRestore() : appDelegate.requestMaximizeHorizontally()
2091 onMaximizeVerticallyClicked: {
2092 if (appDelegate.canBeMaximizedVertically) {
2093 appDelegate.maximizedVertically ? appDelegate.requestRestore() : appDelegate.requestMaximizeVertically()
2096 onMinimizeClicked: { appDelegate.requestMinimize(); }
2097 onDecorationPressed: { appDelegate.activate(); }
2098 onDecorationReleased: fakeRectangle.visible ? fakeRectangle.commit() : appDelegate.updateRestoredGeometry()
2100 property real angle: 0
2101 Behavior on angle { enabled: priv.closingIndex >= 0; LomiriNumberAnimation {} }
2102 property real itemScale: 1
2103 Behavior on itemScale { enabled: priv.closingIndex >= 0; LomiriNumberAnimation {} }
2108 origin.y: decoratedWindow.implicitHeight / 2
2109 xScale: decoratedWindow.itemScale
2110 yScale: decoratedWindow.itemScale
2113 origin { x: 0; y: (decoratedWindow.height / 2) }
2114 axis { x: 0; y: 1; z: 0 }
2115 angle: decoratedWindow.angle
2122 anchors.fill: decoratedWindow
2125 WindowControlsOverlay {
2127 anchors.fill: appDelegate
2129 resizeArea: resizeArea
2132 boundsItem: root.availableDesktopArea
2134 onFakeMaximizeAnimationRequested: if (!appDelegate.maximized) fakeRectangle.maximize(amount, true)
2135 onFakeMaximizeLeftAnimationRequested: if (!appDelegate.maximizedLeft) fakeRectangle.maximizeLeft(amount, true)
2136 onFakeMaximizeRightAnimationRequested: if (!appDelegate.maximizedRight) fakeRectangle.maximizeRight(amount, true)
2137 onFakeMaximizeTopLeftAnimationRequested: if (!appDelegate.maximizedTopLeft) fakeRectangle.maximizeTopLeft(amount, true);
2138 onFakeMaximizeTopRightAnimationRequested: if (!appDelegate.maximizedTopRight) fakeRectangle.maximizeTopRight(amount, true);
2139 onFakeMaximizeBottomLeftAnimationRequested: if (!appDelegate.maximizedBottomLeft) fakeRectangle.maximizeBottomLeft(amount, true);
2140 onFakeMaximizeBottomRightAnimationRequested: if (!appDelegate.maximizedBottomRight) fakeRectangle.maximizeBottomRight(amount, true);
2141 onStopFakeAnimation: fakeRectangle.stop();
2142 onDragReleased: fakeRectangle.visible ? fakeRectangle.commit() : appDelegate.updateRestoredGeometry()
2145 WindowedFullscreenPolicy {
2146 id: windowedFullscreenPolicy
2148 StagedFullscreenPolicy {
2149 id: stagedFullscreenPolicy
2150 active: root.mode == "staged" || root.mode == "stagedWithSideStage"
2151 surface: model.window.surface
2154 SpreadDelegateInputArea {
2156 objectName: "dragArea"
2157 anchors.fill: decoratedWindow
2161 dragDelegate: fakeDragItem
2164 spreadItem.highlightedIndex = index;
2165 if (distance == 0) {
2166 priv.goneToSpread = false;
2170 priv.closingIndex = index
2171 appDelegate.close();
2177 objectName: "windowInfoItem"
2178 anchors { left: parent.left; top: decoratedWindow.bottom; topMargin: units.gu(1) }
2179 title: model.application.name
2180 iconSource: model.application.icon
2181 height: spreadItem.appInfoHeight
2184 visible: opacity > 0
2186 var nextApp = appRepeater.itemAt(index + 1);
2188 return Math.max(iconHeight, nextApp.x - appDelegate.x - units.gu(1))
2190 return appDelegate.width;
2194 spreadItem.highlightedIndex = index;
2195 priv.goneToSpread = false;
2201 objectName: "closeMouseArea"
2202 anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset }
2203 readonly property var mousePos: hoverMouseArea.mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
2204 readonly property bool shown: dragArea.distance == 0
2205 && index == spreadItem.highlightedIndex
2206 && mousePos.y < (decoratedWindow.height / 3)
2207 && mousePos.y > -units.gu(4)
2208 && mousePos.x > -units.gu(4)
2209 && mousePos.x < (decoratedWindow.width * 2 / 3)
2210 opacity: shown ? 1 : 0
2211 visible: opacity > 0
2212 Behavior on opacity { LomiriNumberAnimation { duration: LomiriAnimation.SnapDuration } }
2217 priv.closingIndex = index;
2218 appDelegate.close();
2222 source: "graphics/window-close.svg"
2223 anchors.fill: closeMouseArea
2224 anchors.margins: units.gu(2)
2225 sourceSize.width: width
2226 sourceSize.height: height
2231 // Group all child windows in this item so that we can fade them out together when going to the spread
2232 // (and fade them in back again when returning from it)
2233 readonly property bool stageOnProperState: root.state === "windowed"
2234 || root.state === "staged"
2235 || root.state === "stagedWithSideStage"
2237 // TODO: Is it worth the extra cost of layering to avoid the opacity artifacts of intersecting children?
2238 // Btw, will involve more than uncommenting the line below as children won't necessarily fit this item's
2239 // geometry. This is just a reference.
2240 //layer.enabled: opacity !== 0.0 && opacity !== 1.0
2242 opacity: stageOnProperState ? 1.0 : 0.0
2243 visible: opacity !== 0.0 // make it transparent to input as well
2244 Behavior on opacity { LomiriNumberAnimation {} }
2247 id: childWindowRepeater
2248 model: appDelegate.surface ? appDelegate.surface.childSurfaceList : null
2250 delegate: ChildWindowTree {
2251 surface: model.surface
2253 // Account for the displacement caused by window decoration in the top-level surface
2254 // Ie, the top-level surface is not positioned at (0,0) of this ChildWindow's parent (appDelegate)
2255 displacementX: appDelegate.clientAreaItem.x
2256 displacementY: appDelegate.clientAreaItem.y
2258 boundsItem: root.availableDesktopArea
2259 decorationHeight: priv.windowDecorationHeight
2261 z: childWindowRepeater.count - model.index
2265 // some child surface in this tree got focus.
2266 // Ensure we also have it at the top-level hierarchy
2267 appDelegate.claimFocus();
2277 FakeMaximizeDelegate {
2279 target: priv.focusedAppDelegate
2280 leftMargin: root.availableDesktopArea.x
2281 appContainerWidth: appContainer.width
2282 appContainerHeight: appContainer.height
2283 panelState: root.panelState
2287 id: workspaceSwitcher
2288 enabled: workspaceEnabled
2289 anchors.centerIn: parent
2290 height: units.gu(20)
2291 width: root.width - units.gu(8)
2292 background: root.background
2293 launcherLockedVisible: root.launcherLockedVisible
2294 topPanelHeight: root.topPanelHeight
2297 appContainer.focus = true;
2303 id: shortRightEdgeSwipeAnimation
2306 duration: priv.animationDuration
2310 id: rightEdgeDragArea
2311 objectName: "rightEdgeDragArea"
2312 direction: Direction.Leftwards
2313 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
2314 width: root.dragAreaWidth
2315 enabled: root.spreadEnabled
2317 property var gesturePoints: []
2318 property bool cancelled: false
2320 property real progress: -touchPosition.x / root.width
2321 onProgressChanged: {
2323 draggedProgress = progress;
2327 property real draggedProgress: 0
2329 onTouchPositionChanged: {
2330 gesturePoints.push(touchPosition.x);
2331 if (gesturePoints.length > 10) {
2332 gesturePoints.splice(0, gesturePoints.length - 10)
2336 onDraggingChanged: {
2338 // A potential edge-drag gesture has started. Start recording it
2341 draggedProgress = 0;
2343 // Ok. The user released. Did he drag far enough to go to full spread?
2344 if (gesturePoints[gesturePoints.length - 1] < -spreadItem.rightEdgeBreakPoint * spreadItem.width ) {
2346 // He dragged far enough, but if the last movement was a flick to the right again, he wants to cancel the spread again.
2347 var oneWayFlickToRight = true;
2348 var smallestX = gesturePoints[0]-1;
2349 for (var i = 0; i < gesturePoints.length; i++) {
2350 if (gesturePoints[i] <= smallestX) {
2351 oneWayFlickToRight = false;
2354 smallestX = gesturePoints[i];
2357 if (!oneWayFlickToRight) {
2358 // Ok, the user made it, let's go to spread!
2359 priv.goneToSpread = true;
2364 // Ok, the user didn't drag far enough to cross the breakPoint
2365 // Find out if it was a one-way movement to the left, in which case we just switch directly to next app.
2366 var oneWayFlick = true;
2367 var smallestX = rightEdgeDragArea.width;
2368 for (var i = 0; i < gesturePoints.length; i++) {
2369 if (gesturePoints[i] >= smallestX) {
2370 oneWayFlick = false;
2373 smallestX = gesturePoints[i];
2376 if (appRepeater.count > 1 &&
2377 (oneWayFlick && rightEdgeDragArea.distance > units.gu(2) || rightEdgeDragArea.distance > spreadItem.rightEdgeBreakPoint * spreadItem.width)) {
2378 var nextStage = appRepeater.itemAt(priv.nextInStack).stage
2379 for (var i = 0; i < appRepeater.count; i++) {
2380 if (i != priv.nextInStack && appRepeater.itemAt(i).stage == nextStage) {
2381 appRepeater.itemAt(i).playHidingAnimation()
2385 appRepeater.itemAt(priv.nextInStack).playFocusAnimation()
2386 if (appRepeater.itemAt(priv.nextInStack).stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
2399 GestureAreaSizeHint {
2400 anchors.fill: parent
2404 TabletSideStageTouchGesture {
2406 objectName: "triGestureArea"
2407 anchors.fill: parent
2409 property Item appDelegate
2411 dragComponent: dragComponent
2412 dragComponentProperties: { "appDelegate": appDelegate }
2415 function matchDelegate(obj) { return String(obj.objectName).indexOf("appDelegate") >= 0; }
2417 var delegateAtCenter = Functions.itemAt(appContainer, x, y, matchDelegate);
2418 if (!delegateAtCenter) return;
2420 appDelegate = delegateAtCenter;
2424 if (sideStage.shown) {
2428 priv.updateMainAndSideStageIndexes()
2433 // If we're dragging to the sidestage.
2434 if (!sideStage.shown) {
2440 // Hide side stage if the app drag was cancelled
2441 if (!priv.sideStageDelegate) {
2449 property Item appDelegate
2451 surface: appDelegate ? appDelegate.surface : null
2453 consumesInput: false
2456 requestedWidth: appDelegate ? appDelegate.requestedWidth : 0
2457 requestedHeight: appDelegate ? appDelegate.requestedHeight : 0
2460 height: units.gu(40)
2462 Drag.hotSpot.x: width/2
2463 Drag.hotSpot.y: height/2
2464 // only accept opposite stage.
2466 if (!surface) return "Disabled";
2468 if (appDelegate.stage === ApplicationInfo.MainStage) {
2469 if (appDelegate.application.supportedOrientations
2470 & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {