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/>.
19import QtQuick.Window 2.2
20import Lomiri.Components 1.3
21import QtMir.Application 0.1
22import "../Components/PanelState"
25import Lomiri.Gestures 0.1
26import GlobalShortcut 1.0
29import "Spread/MathUtils.js" as MathUtils
30import ProcessControl 0.1
31import WindowManager 1.0
37 property QtObject applicationManager
38 property QtObject topLevelSurfaceList
39 property bool altTabPressed
40 property url background
41 property alias backgroundSourceSize: wallpaper.sourceSize
42 property int dragAreaWidth
43 property real nativeHeight
44 property real nativeWidth
45 property QtObject orientations
46 property int shellOrientation
47 property int shellOrientationAngle
48 property bool spreadEnabled: true // If false, animations and right edge will be disabled
49 property bool suspended
50 property bool oskEnabled: false
51 property rect inputMethodRect
52 property real rightEdgePushProgress: 0
53 property Item availableDesktopArea
54 property PanelState panelState
56 // Whether outside forces say that the Stage may have focus
57 property bool allowInteractivity
59 readonly property bool interactive: (state === "staged" || state === "stagedWithSideStage" || state === "windowed") && allowInteractivity
62 property string mode: "staged"
64 readonly property var temporarySelectedWorkspace: state == "spread" ? screensAndWorkspaces.activeWorkspace : null
65 property bool workspaceEnabled: (mode == "windowed" && settings.enableWorkspace) || settings.forceEnableWorkspace
67 // Used by the tutorial code
68 readonly property real rightEdgeDragProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0 // How far left the stage has been dragged
70 // used by the snap windows (edge maximize) feature
71 readonly property alias previewRectangle: fakeRectangle
73 readonly property bool spreadShown: state == "spread"
74 readonly property var mainApp: priv.focusedAppDelegate ? priv.focusedAppDelegate.application : null
76 // application windows never rotate independently
77 property int mainAppWindowOrientationAngle: shellOrientationAngle
79 property bool orientationChangesEnabled: !priv.focusedAppDelegate || priv.focusedAppDelegate.orientationChangesEnabled
81 property int supportedOrientations: {
85 return mainApp.supportedOrientations;
86 case "stagedWithSideStage":
87 var orientations = mainApp.supportedOrientations;
88 orientations |= Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation;
89 if (priv.sideStageItemId) {
90 // If we have a sidestage app, support Portrait orientation
91 // so that it will switch the sidestage app to mainstage on rotate to portrait
92 orientations |= Qt.PortraitOrientation|Qt.InvertedPortraitOrientation;
98 return Qt.PortraitOrientation |
99 Qt.LandscapeOrientation |
100 Qt.InvertedPortraitOrientation |
101 Qt.InvertedLandscapeOrientation;
106 schema.id: "com.lomiri.Shell"
109 property int launcherLeftMargin : 0
112 target: topLevelSurfaceList
113 property: "rootFocus"
117 onInteractiveChanged: {
118 // Stage must have focus before activating windows, including null
124 onAltTabPressedChanged: {
127 if (root.spreadEnabled) {
128 altTabDelayTimer.start();
131 // Alt Tab has been released, did we already go to spread?
132 if (priv.goneToSpread) {
133 priv.goneToSpread = false;
135 // No we didn't, do a quick alt-tab
136 if (appRepeater.count > 1) {
137 appRepeater.itemAt(1).activate();
138 } else if (appRepeater.count > 0) {
139 appRepeater.itemAt(0).activate(); // quick alt-tab to the only (minimized) window should still activate it
150 if (root.altTabPressed) {
151 priv.goneToSpread = true;
156 // For MirAL window management
158 normal: Qt.rect(0, root.mode === "windowed" ? priv.windowDecorationHeight : 0, 0, 0)
162 property Item itemConfiningMouseCursor: !spreadShown && priv.focusedAppDelegate && priv.focusedAppDelegate.window && priv.focusedAppDelegate.window.confinesMousePointer ?
163 priv.focusedAppDelegate.clientAreaItem : null;
165 signal itemSnapshotRequested(Item item)
167 // functions to be called from outside
168 function updateFocusedAppOrientation() { /* TODO */ }
169 function updateFocusedAppOrientationAnimated() { /* TODO */}
171 function closeSpread() {
172 spreadItem.highlightedIndex = -1;
173 priv.goneToSpread = false;
176 onSpreadEnabledChanged: {
177 if (!spreadEnabled && spreadShown) {
182 onRightEdgePushProgressChanged: {
183 if (spreadEnabled && rightEdgePushProgress >= 1) {
184 priv.goneToSpread = true
189 id: lifecycleExceptions
190 schema.id: "com.canonical.qtmir"
193 function isExemptFromLifecycle(appId) {
194 var shortAppId = appId.split('_')[0];
195 for (var i = 0; i < lifecycleExceptions.lifecycleExemptAppids.length; i++) {
196 if (shortAppId === lifecycleExceptions.lifecycleExemptAppids[i]) {
204 id: closeFocusedShortcut
205 shortcut: Qt.AltModifier|Qt.Key_F4
207 if (priv.focusedAppDelegate) {
208 priv.focusedAppDelegate.close();
214 id: showSpreadShortcut
215 shortcut: Qt.MetaModifier|Qt.Key_W
216 active: root.spreadEnabled
217 onTriggered: priv.goneToSpread = true
221 id: minimizeAllShortcut
222 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_D
223 onTriggered: priv.minimizeAllWindows()
224 active: root.state == "windowed"
228 id: maximizeWindowShortcut
229 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up
230 onTriggered: priv.focusedAppDelegate.requestMaximize()
231 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximized
235 id: maximizeWindowLeftShortcut
236 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left
237 onTriggered: priv.focusedAppDelegate.requestMaximizeLeft()
238 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight
242 id: maximizeWindowRightShortcut
243 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right
244 onTriggered: priv.focusedAppDelegate.requestMaximizeRight()
245 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight
249 id: minimizeRestoreShortcut
250 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
252 if (priv.focusedAppDelegate.anyMaximized) {
253 priv.focusedAppDelegate.requestRestore();
255 priv.focusedAppDelegate.requestMinimize();
258 active: root.state == "windowed" && priv.focusedAppDelegate
262 shortcut: Qt.AltModifier|Qt.Key_Print
263 onTriggered: root.itemSnapshotRequested(priv.focusedAppDelegate)
264 active: priv.focusedAppDelegate !== null
268 shortcut: Qt.ControlModifier|Qt.AltModifier|Qt.Key_T
270 // try in this order: snap pkg, new deb name, old deb name
271 var candidates = ["lomiri-terminal-app_lomiri-terminal-app", "lomiri-terminal-app", "com.lomiri.terminal_terminal"];
272 for (var i = 0; i < candidates.length; i++) {
273 if (priv.startApp(candidates[i]))
280 id: showWorkspaceSwitcherShortcutLeft
281 shortcut: Qt.AltModifier|Qt.ControlModifier|Qt.Key_Left
282 active: !workspaceSwitcher.active
285 workspaceSwitcher.showLeft()
289 id: showWorkspaceSwitcherShortcutRight
290 shortcut: Qt.AltModifier|Qt.ControlModifier|Qt.Key_Right
291 active: !workspaceSwitcher.active
294 workspaceSwitcher.showRight()
298 id: showWorkspaceSwitcherShortcutUp
299 shortcut: Qt.AltModifier|Qt.ControlModifier|Qt.Key_Up
300 active: !workspaceSwitcher.active
303 workspaceSwitcher.showUp()
307 id: showWorkspaceSwitcherShortcutDown
308 shortcut: Qt.AltModifier|Qt.ControlModifier|Qt.Key_Down
309 active: !workspaceSwitcher.active
312 workspaceSwitcher.showDown()
318 objectName: "DesktopStagePrivate"
320 function startApp(appId) {
321 if (root.applicationManager.findApplication(appId)) {
322 return root.applicationManager.requestFocusApplication(appId);
324 return root.applicationManager.startApplication(appId) !== null;
328 property var focusedAppDelegate: null
329 property var foregroundMaximizedAppDelegate: null // for stuff like drop shadow and focusing maximized app by clicking panel
331 property bool goneToSpread: false
332 property int closingIndex: -1
333 property int animationDuration: LomiriAnimation.FastDuration
335 function updateForegroundMaximizedApp() {
337 for (var i = 0; i < appRepeater.count && !found; i++) {
338 var item = appRepeater.itemAt(i);
339 if (item && item.visuallyMaximized) {
340 foregroundMaximizedAppDelegate = item;
345 foregroundMaximizedAppDelegate = null;
349 function minimizeAllWindows() {
350 for (var i = appRepeater.count - 1; i >= 0; i--) {
351 var appDelegate = appRepeater.itemAt(i);
352 if (appDelegate && !appDelegate.minimized) {
353 appDelegate.requestMinimize();
358 readonly property bool sideStageEnabled: root.mode === "stagedWithSideStage" &&
359 (root.shellOrientation == Qt.LandscapeOrientation ||
360 root.shellOrientation == Qt.InvertedLandscapeOrientation)
361 onSideStageEnabledChanged: {
362 for (var i = 0; i < appRepeater.count; i++) {
363 appRepeater.itemAt(i).refreshStage();
365 priv.updateMainAndSideStageIndexes();
368 property var mainStageDelegate: null
369 property var sideStageDelegate: null
370 property int mainStageItemId: 0
371 property int sideStageItemId: 0
372 property string mainStageAppId: ""
373 property string sideStageAppId: ""
375 onSideStageDelegateChanged: {
376 if (!sideStageDelegate) {
381 function updateMainAndSideStageIndexes() {
382 if (root.mode != "stagedWithSideStage") {
383 priv.sideStageDelegate = null;
384 priv.sideStageItemId = 0;
385 priv.sideStageAppId = "";
386 priv.mainStageDelegate = appRepeater.itemAt(0);
387 priv.mainStageItemId = topLevelSurfaceList.idAt(0);
388 priv.mainStageAppId = topLevelSurfaceList.applicationAt(0) ? topLevelSurfaceList.applicationAt(0).appId : ""
392 var choseMainStage = false;
393 var choseSideStage = false;
395 if (!root.topLevelSurfaceList)
398 for (var i = 0; i < appRepeater.count && (!choseMainStage || !choseSideStage); ++i) {
399 var appDelegate = appRepeater.itemAt(i);
401 // This might happen during startup phase... If the delegate appears and claims focus
402 // things are updated and appRepeater.itemAt(x) still returns null while appRepeater.count >= x
403 // Lets just skip it, on startup it will be generated at a later point too...
406 if (sideStage.shown && appDelegate.stage == ApplicationInfoInterface.SideStage
407 && !choseSideStage) {
408 priv.sideStageDelegate = appDelegate
409 priv.sideStageItemId = root.topLevelSurfaceList.idAt(i);
410 priv.sideStageAppId = root.topLevelSurfaceList.applicationAt(i).appId;
411 choseSideStage = true;
412 } else if (!choseMainStage && appDelegate.stage == ApplicationInfoInterface.MainStage) {
413 priv.mainStageDelegate = appDelegate;
414 priv.mainStageItemId = root.topLevelSurfaceList.idAt(i);
415 priv.mainStageAppId = root.topLevelSurfaceList.applicationAt(i).appId;
416 choseMainStage = true;
419 if (!choseMainStage && priv.mainStageDelegate) {
420 priv.mainStageDelegate = null;
421 priv.mainStageItemId = 0;
422 priv.mainStageAppId = "";
424 if (!choseSideStage && priv.sideStageDelegate) {
425 priv.sideStageDelegate = null;
426 priv.sideStageItemId = 0;
427 priv.sideStageAppId = "";
431 property int nextInStack: {
432 var mainStageIndex = priv.mainStageDelegate ? priv.mainStageDelegate.itemIndex : -1;
433 var sideStageIndex = priv.sideStageDelegate ? priv.sideStageDelegate.itemIndex : -1;
434 if (sideStageIndex == -1) {
435 return topLevelSurfaceList.count > 1 ? 1 : -1;
437 if (mainStageIndex == 0 || sideStageIndex == 0) {
438 if (mainStageIndex == 1 || sideStageIndex == 1) {
439 return topLevelSurfaceList.count > 2 ? 2 : -1;
446 readonly property real virtualKeyboardHeight: root.inputMethodRect.height
448 readonly property real windowDecorationHeight: units.gu(3)
451 Component.onCompleted: priv.updateMainAndSideStageIndexes()
455 onCloseClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
456 onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestMinimize(); } }
457 onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestRestore(); } }
462 property: "decorationsVisible"
463 value: mode == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.maximized && !root.spreadShown
470 if (priv.focusedAppDelegate !== null) {
471 if (priv.focusedAppDelegate.maximized)
472 return priv.focusedAppDelegate.title
474 return priv.focusedAppDelegate.appName
478 when: priv.focusedAppDelegate
483 property: "focusedPersistentSurfaceId"
485 if (priv.focusedAppDelegate !== null) {
486 if (priv.focusedAppDelegate.surface) {
487 return priv.focusedAppDelegate.surface.persistentId;
492 when: priv.focusedAppDelegate
497 property: "dropShadow"
498 value: priv.focusedAppDelegate && !priv.focusedAppDelegate.maximized && priv.foregroundMaximizedAppDelegate !== null && mode == "windowed"
503 property: "closeButtonShown"
504 value: priv.focusedAppDelegate && priv.focusedAppDelegate.maximized
507 Component.onDestruction: {
508 panelState.title = "";
509 panelState.decorationsVisible = false;
510 panelState.dropShadow = false;
514 model: root.applicationManager
516 id: applicationDelegate
517 // TODO: figure out some lifecycle policy, like suspending minimized apps
518 // or something if running windowed.
519 // TODO: If the device has a dozen suspended apps because it was running
520 // in staged mode, when it switches to Windowed mode it will suddenly
521 // resume all those apps at once. We might want to avoid that.
522 property var requestedState: root.mode === "windowed"
523 || (!root.suspended && model.application && priv.focusedAppDelegate &&
524 (priv.focusedAppDelegate.appId === model.application.appId ||
525 priv.mainStageAppId === model.application.appId ||
526 priv.sideStageAppId === model.application.appId))
527 ? ApplicationInfoInterface.RequestedRunning
528 : ApplicationInfoInterface.RequestedSuspended
529 property bool temporaryAwaken: ProcessControl.awakenProcesses.indexOf(model.application.appId) >= 0
531 property var stateBinding: Binding {
532 target: model.application
533 property: "requestedState"
534 value: applicationDelegate.requestedState
537 property var lifecycleBinding: Binding {
538 target: model.application
539 property: "exemptFromLifecycle"
540 value: model.application
541 ? (!model.application.isTouchApp ||
542 isExemptFromLifecycle(model.application.appId) ||
543 applicationDelegate.temporaryAwaken)
547 property var focusRequestedConnection: Connections {
548 target: model.application
551 // Application emits focusRequested when it has no surface (i.e. their processes died).
552 // Find the topmost window for this application and activate it, after which the app
553 // will be requested to be running.
555 for (var i = 0; i < appRepeater.count; i++) {
556 var appDelegate = appRepeater.itemAt(i);
557 if (appDelegate.application.appId === model.application.appId) {
558 appDelegate.activate();
563 console.warn("Application requested te be focused but no window for it. What should we do?");
571 name: "spread"; when: priv.goneToSpread
572 PropertyChanges { target: floatingFlickable; enabled: true }
573 PropertyChanges { target: root; focus: true }
574 PropertyChanges { target: spreadItem; focus: true }
575 PropertyChanges { target: hoverMouseArea; enabled: true }
576 PropertyChanges { target: rightEdgeDragArea; enabled: false }
577 PropertyChanges { target: cancelSpreadMouseArea; enabled: true }
578 PropertyChanges { target: noAppsRunningHint; visible: (root.topLevelSurfaceList.count < 1) }
579 PropertyChanges { target: blurLayer; visible: true; blurRadius: 32; brightness: .65; opacity: 1 }
580 PropertyChanges { target: wallpaper; visible: false }
581 PropertyChanges { target: screensAndWorkspaces; opacity: 1 }
584 name: "stagedRightEdge"; when: root.spreadEnabled && (rightEdgeDragArea.dragging || rightEdgePushProgress > 0) && root.mode == "staged"
592 PropertyChanges { target: noAppsRunningHint; visible: (root.topLevelSurfaceList.count < 1) }
595 name: "sideStagedRightEdge"; when: root.spreadEnabled && (rightEdgeDragArea.dragging || rightEdgePushProgress > 0) && root.mode == "stagedWithSideStage"
596 extend: "stagedRightEdge"
599 opacity: priv.sideStageDelegate && priv.sideStageDelegate.x === sideStage.x ? 1 : 0
604 name: "windowedRightEdge"; when: root.spreadEnabled && (rightEdgeDragArea.dragging || rightEdgePushProgress > 0) && root.mode == "windowed"
610 opacity: MathUtils.linearAnimation(spreadItem.rightEdgeBreakPoint, 1, 0, 1, Math.max(rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0, rightEdgePushProgress))
614 name: "staged"; when: root.mode === "staged"
615 PropertyChanges { target: root; focus: true }
616 PropertyChanges { target: appContainer; focus: true }
619 name: "stagedWithSideStage"; when: root.mode === "stagedWithSideStage"
620 PropertyChanges { target: triGestureArea; enabled: priv.sideStageEnabled }
621 PropertyChanges { target: sideStage; visible: true }
622 PropertyChanges { target: root; focus: true }
623 PropertyChanges { target: appContainer; focus: true }
626 name: "windowed"; when: root.mode === "windowed"
627 PropertyChanges { target: root; focus: true }
628 PropertyChanges { target: appContainer; focus: true }
633 from: "stagedRightEdge,sideStagedRightEdge,windowedRightEdge"; to: "spread"
634 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
635 PropertyAction { target: screensAndWorkspaces; property: "activeWorkspace"; value: WMScreen.currentWorkspace }
636 PropertyAnimation { target: blurLayer; properties: "brightness,blurRadius"; duration: priv.animationDuration }
637 LomiriNumberAnimation { target: screensAndWorkspaces; property: "opacity"; duration: priv.animationDuration }
641 PropertyAction { target: screensAndWorkspaces; property: "activeWorkspace"; value: WMScreen.currentWorkspace }
642 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: appRepeater.count > 1 ? 1 : 0 }
643 PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
644 LomiriNumberAnimation { target: screensAndWorkspaces; property: "opacity"; duration: priv.animationDuration }
648 SequentialAnimation {
651 var item = appRepeater.itemAt(Math.max(0, spreadItem.highlightedIndex));
653 if (item.stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
656 item.playFocusAnimation();
660 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
661 PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
665 to: "stagedRightEdge,sideStagedRightEdge"
666 PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
669 to: "stagedWithSideStage"
670 ScriptAction { script: priv.updateMainAndSideStageIndexes(); }
676 id: cancelSpreadMouseArea
679 onClicked: priv.goneToSpread = false
684 objectName: "appContainer"
690 objectName: "stageBackground"
692 source: root.background
693 // Make sure it's the lowest item. Due to the left edge drag we sometimes need
694 // to put the dash at -1 and we don't want it behind the Wallpaper
705 ScreensAndWorkspaces {
706 id: screensAndWorkspaces
707 anchors { left: parent.left; top: parent.top; right: parent.right; leftMargin: root.launcherLeftMargin }
708 height: Math.max(units.gu(30), parent.height * .3)
709 background: root.background
711 visible: workspaceEnabled ? opacity > 0 : false
712 enabled: workspaceEnabled
714 onCloseSpread: priv.goneToSpread = false;
719 objectName: "spreadItem"
722 bottom: parent.bottom;
724 top: workspaceEnabled ? screensAndWorkspaces.bottom : parent.top;
726 leftMargin: root.availableDesktopArea.x
727 model: root.topLevelSurfaceList
728 spreadFlickable: floatingFlickable
732 priv.goneToSpread = false;
736 appRepeater.itemAt(highlightedIndex).close();
740 id: floatingFlickable
741 objectName: "spreadFlickable"
744 contentWidth: spreadItem.spreadTotalWidth
746 function snap(toIndex) {
747 var delegate = appRepeater.itemAt(toIndex)
748 var targetContentX = floatingFlickable.contentWidth / spreadItem.totalItemCount * toIndex;
749 if (targetContentX - floatingFlickable.contentX > spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) {
750 var offset = (spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) - (targetContentX - floatingFlickable.contentX)
751 snapAnimation.to = floatingFlickable.contentX - offset;
752 snapAnimation.start();
753 } else if (targetContentX - floatingFlickable.contentX < spreadItem.leftStackXPos + units.gu(1)) {
754 var offset = (spreadItem.leftStackXPos + units.gu(1)) - (targetContentX - floatingFlickable.contentX);
755 snapAnimation.to = floatingFlickable.contentX - offset;
756 snapAnimation.start();
759 LomiriNumberAnimation {id: snapAnimation; target: floatingFlickable; property: "contentX"}
764 objectName: "hoverMouseArea"
766 propagateComposedEvents: true
770 property bool wasTouchPress: false
772 property int scrollAreaWidth: width / 3
773 property bool progressiveScrollingEnabled: false
776 mouse.accepted = false
778 if (hoverMouseArea.pressed || wasTouchPress) {
782 // Find the hovered item and mark it active
783 for (var i = appRepeater.count - 1; i >= 0; i--) {
784 var appDelegate = appRepeater.itemAt(i);
785 var mapped = mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
786 var itemUnder = appDelegate.childAt(mapped.x, mapped.y);
787 if (itemUnder && (itemUnder.objectName === "dragArea" || itemUnder.objectName === "windowInfoItem" || itemUnder.objectName == "closeMouseArea")) {
788 spreadItem.highlightedIndex = i;
793 if (floatingFlickable.contentWidth > floatingFlickable.width) {
794 var margins = floatingFlickable.width * 0.05;
796 if (!progressiveScrollingEnabled && mouseX < floatingFlickable.width - scrollAreaWidth) {
797 progressiveScrollingEnabled = true
800 // do we need to scroll?
801 if (mouseX < scrollAreaWidth + margins) {
802 var progress = Math.min(1, (scrollAreaWidth + margins - mouseX) / (scrollAreaWidth - margins));
803 var contentX = (1 - progress) * (floatingFlickable.contentWidth - floatingFlickable.width)
804 floatingFlickable.contentX = Math.max(0, Math.min(floatingFlickable.contentX, contentX))
806 if (mouseX > floatingFlickable.width - scrollAreaWidth && progressiveScrollingEnabled) {
807 var progress = Math.min(1, (mouseX - (floatingFlickable.width - scrollAreaWidth)) / (scrollAreaWidth - margins))
808 var contentX = progress * (floatingFlickable.contentWidth - floatingFlickable.width)
809 floatingFlickable.contentX = Math.min(floatingFlickable.contentWidth - floatingFlickable.width, Math.max(floatingFlickable.contentX, contentX))
815 mouse.accepted = false;
816 wasTouchPress = mouse.source === Qt.MouseEventSynthesizedByQt;
819 onExited: wasTouchPress = false;
824 id: noAppsRunningHint
826 anchors.horizontalCenter: parent.horizontalCenter
827 anchors.verticalCenter: parent.verticalCenter
829 horizontalAlignment: Qt.AlignHCenter
830 verticalAlignment: Qt.AlignVCenter
831 anchors.leftMargin: root.launcherLeftMargin
832 wrapMode: Label.WordWrap
834 text: i18n.tr("No running apps")
838 target: root.topLevelSurfaceList
839 onListChanged: priv.updateMainAndSideStageIndexes()
844 objectName: "MainStageDropArea"
848 bottom: parent.bottom
850 width: appContainer.width - sideStage.width
851 enabled: priv.sideStageEnabled
854 drop.source.appDelegate.saveStage(ApplicationInfoInterface.MainStage);
855 drop.source.appDelegate.focus = true;
862 objectName: "sideStage"
864 height: appContainer.height
865 x: appContainer.width - width
867 Behavior on opacity { LomiriNumberAnimation {} }
869 if (!priv.mainStageItemId) return 0;
871 if (priv.sideStageItemId && priv.nextInStack > 0) {
873 // Due the order in which bindings are evaluated, this might be triggered while shuffling
874 // the list and index doesn't yet match with itemIndex (even though itemIndex: index)
875 // Let's walk the list and compare itemIndex to make sure we have the correct one.
876 var nextDelegateInStack = -1;
877 for (var i = 0; i < appRepeater.count; i++) {
878 if (appRepeater.itemAt(i).itemIndex == priv.nextInStack) {
879 nextDelegateInStack = appRepeater.itemAt(i);
884 if (nextDelegateInStack.stage === ApplicationInfoInterface.MainStage) {
885 // if the next app in stack is a main stage app, put the sidestage on top of it.
895 if (!shown && priv.mainStageDelegate && !root.spreadShown) {
896 priv.mainStageDelegate.activate();
901 id: sideStageDropArea
902 objectName: "SideStageDropArea"
905 property bool dropAllowed: true
908 dropAllowed = drag.keys != "Disabled";
914 if (drop.keys == "MainStage") {
915 drop.source.appDelegate.saveStage(ApplicationInfoInterface.SideStage);
916 drop.source.appDelegate.focus = true;
921 if (!sideStageDropArea.drag.source) {
931 property real previewScale: .5
932 height: (screensAndWorkspaces.height - units.gu(8)) / 2
934 width: implicitWidth * height / implicitHeight
937 opacity: surface != null ? 1 : 0
938 Behavior on opacity { LomiriNumberAnimation {} }
940 enabled: workspaceSwitcher
942 Drag.active: surface != null
943 Drag.keys: ["application"]
950 model: topLevelSurfaceList
951 objectName: "appRepeater"
953 function indexOf(delegateItem) {
954 for (var i = 0; i < count; i++) {
955 if (itemAt(i) === delegateItem) {
962 delegate: FocusScope {
964 objectName: "appDelegate_" + model.window.id
965 property int itemIndex: index // We need this from outside the repeater
966 // z might be overriden in some cases by effects, but we need z ordering
967 // to calculate occlusion detection
968 property int normalZ: topLevelSurfaceList.count - index
970 if (visuallyMaximized) {
971 priv.updateForegroundMaximizedApp();
976 opacity: fakeDragItem.surface == model.window.surface && fakeDragItem.Drag.active ? 0 : 1
977 Behavior on opacity { LomiriNumberAnimation {} }
979 // Set these as propertyes as they wont update otherwise
980 property real screenOffsetX: Screen.virtualX
981 property real screenOffsetY: Screen.virtualY
983 // Normally we want x/y where the surface thinks it is. Width/height of our delegate will
984 // match what the actual surface size is.
985 // Don't write to those, they will be set by states
987 // Here we will also need to remove the screen offset from miral's results
988 // as lomiri x,y will be relative to the current screen only
989 // FIXME: when proper multiscreen lands
990 x: model.window.position.x - clientAreaItem.x - screenOffsetX
991 y: model.window.position.y - clientAreaItem.y - screenOffsetY
992 width: decoratedWindow.implicitWidth
993 height: decoratedWindow.implicitHeight
995 // requestedX/Y/width/height is what we ask the actual surface to be.
996 // Do not write to those, they will be set by states
997 property real requestedX: windowedX
998 property real requestedY: windowedY
999 property real requestedWidth: windowedWidth
1000 property real requestedHeight: windowedHeight
1002 // For both windowed and staged need to tell miral what screen we are on,
1003 // so we need to add the screen offset to the position we tell miral
1004 // FIXME: when proper multiscreen lands
1006 target: model.window; property: "requestedPosition"
1007 // miral doesn't know about our window decorations. So we have to deduct them
1008 value: Qt.point(appDelegate.requestedX + appDelegate.clientAreaItem.x + screenOffsetX,
1009 appDelegate.requestedY + appDelegate.clientAreaItem.y + screenOffsetY)
1010 when: root.mode == "windowed"
1013 target: model.window; property: "requestedPosition"
1014 value: Qt.point(screenOffsetX, screenOffsetY)
1015 when: root.mode != "windowed"
1018 // In those are for windowed mode. Those values basically store the window's properties
1019 // when having a floating window. If you want to move/resize a window in normal mode, this is what you want to write to.
1020 property real windowedX
1021 property real windowedY
1022 property real windowedWidth
1023 property real windowedHeight
1025 // unlike windowedX/Y, this is the last known grab position before being pushed against edges/corners
1026 // when restoring, the window should return to these, not to the place where it was dropped near the edge
1027 property real restoredX
1028 property real restoredY
1030 // Keeps track of the window geometry while in normal or restored state
1031 // Useful when returning from some maxmized state or when saving the geometry while maximized
1032 // FIXME: find a better solution
1033 property real normalX: 0
1034 property real normalY: 0
1035 property real normalWidth: 0
1036 property real normalHeight: 0
1037 function updateNormalGeometry() {
1038 if (appDelegate.state == "normal" || appDelegate.state == "restored") {
1039 normalX = appDelegate.requestedX;
1040 normalY = appDelegate.requestedY;
1041 normalWidth = appDelegate.width;
1042 normalHeight = appDelegate.height;
1045 function updateRestoredGeometry() {
1046 if (appDelegate.state == "normal" || appDelegate.state == "restored") {
1047 // save the x/y to restore to
1048 restoredX = appDelegate.x;
1049 restoredY = appDelegate.y;
1055 onXChanged: appDelegate.updateNormalGeometry();
1056 onYChanged: appDelegate.updateNormalGeometry();
1057 onWidthChanged: appDelegate.updateNormalGeometry();
1058 onHeightChanged: appDelegate.updateNormalGeometry();
1061 // True when the Stage is focusing this app and playing its own animation.
1062 // Stays true until the app is unfocused.
1063 // If it is, we don't want to play the slide in/out transition from StageMaths.
1064 // Setting it imperatively is not great, but any declarative solution hits
1065 // race conditions, causing two animations to play for one focus event.
1066 property bool inhibitSlideAnimation: false
1071 value: appDelegate.requestedY -
1072 Math.min(appDelegate.requestedY - root.availableDesktopArea.y,
1073 Math.max(0, priv.virtualKeyboardHeight - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
1074 when: root.oskEnabled && appDelegate.focus && (appDelegate.state == "normal" || appDelegate.state == "restored")
1075 && root.inputMethodRect.height > 0
1078 Behavior on x { id: xBehavior; enabled: priv.closingIndex >= 0; LomiriNumberAnimation { onRunningChanged: if (!running) priv.closingIndex = -1} }
1082 onShellOrientationAngleChanged: {
1083 // at this point decoratedWindow.surfaceOrientationAngle is the old shellOrientationAngle
1084 if (application && application.rotatesWindowContents) {
1085 if (root.state == "windowed") {
1086 var angleDiff = decoratedWindow.surfaceOrientationAngle - shellOrientationAngle;
1087 angleDiff = (360 + angleDiff) % 360;
1088 if (angleDiff === 90 || angleDiff === 270) {
1089 var aux = decoratedWindow.requestedHeight;
1090 decoratedWindow.requestedHeight = decoratedWindow.requestedWidth + decoratedWindow.actualDecorationHeight;
1091 decoratedWindow.requestedWidth = aux - decoratedWindow.actualDecorationHeight;
1094 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
1096 decoratedWindow.surfaceOrientationAngle = 0;
1101 readonly property alias application: decoratedWindow.application
1102 readonly property alias minimumWidth: decoratedWindow.minimumWidth
1103 readonly property alias minimumHeight: decoratedWindow.minimumHeight
1104 readonly property alias maximumWidth: decoratedWindow.maximumWidth
1105 readonly property alias maximumHeight: decoratedWindow.maximumHeight
1106 readonly property alias widthIncrement: decoratedWindow.widthIncrement
1107 readonly property alias heightIncrement: decoratedWindow.heightIncrement
1109 readonly property bool maximized: windowState === WindowStateStorage.WindowStateMaximized
1110 readonly property bool maximizedLeft: windowState === WindowStateStorage.WindowStateMaximizedLeft
1111 readonly property bool maximizedRight: windowState === WindowStateStorage.WindowStateMaximizedRight
1112 readonly property bool maximizedHorizontally: windowState === WindowStateStorage.WindowStateMaximizedHorizontally
1113 readonly property bool maximizedVertically: windowState === WindowStateStorage.WindowStateMaximizedVertically
1114 readonly property bool maximizedTopLeft: windowState === WindowStateStorage.WindowStateMaximizedTopLeft
1115 readonly property bool maximizedTopRight: windowState === WindowStateStorage.WindowStateMaximizedTopRight
1116 readonly property bool maximizedBottomLeft: windowState === WindowStateStorage.WindowStateMaximizedBottomLeft
1117 readonly property bool maximizedBottomRight: windowState === WindowStateStorage.WindowStateMaximizedBottomRight
1118 readonly property bool anyMaximized: maximized || maximizedLeft || maximizedRight || maximizedHorizontally || maximizedVertically ||
1119 maximizedTopLeft || maximizedTopRight || maximizedBottomLeft || maximizedBottomRight
1121 readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
1122 readonly property bool fullscreen: windowState === WindowStateStorage.WindowStateFullscreen
1124 readonly property bool canBeMaximized: canBeMaximizedHorizontally && canBeMaximizedVertically
1125 readonly property bool canBeMaximizedLeftRight: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
1126 (maximumHeight == 0 || maximumHeight >= appContainer.height)
1127 readonly property bool canBeCornerMaximized: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
1128 (maximumHeight == 0 || maximumHeight >= appContainer.height/2)
1129 readonly property bool canBeMaximizedHorizontally: maximumWidth == 0 || maximumWidth >= appContainer.width
1130 readonly property bool canBeMaximizedVertically: maximumHeight == 0 || maximumHeight >= appContainer.height
1131 readonly property alias orientationChangesEnabled: decoratedWindow.orientationChangesEnabled
1133 // TODO drop our own windowType once Mir/Miral/Qtmir gets in sync with ours
1134 property int windowState: WindowStateStorage.WindowStateNormal
1135 property int prevWindowState: WindowStateStorage.WindowStateRestored
1137 property bool animationsEnabled: true
1138 property alias title: decoratedWindow.title
1139 readonly property string appName: model.application ? model.application.name : ""
1140 property bool visuallyMaximized: false
1141 property bool visuallyMinimized: false
1142 readonly property alias windowedTransitionRunning: windowedTransition.running
1144 property int stage: ApplicationInfoInterface.MainStage
1145 function saveStage(newStage) {
1146 appDelegate.stage = newStage;
1147 WindowStateStorage.saveStage(appId, newStage);
1148 priv.updateMainAndSideStageIndexes()
1151 readonly property var surface: model.window.surface
1152 readonly property var window: model.window
1154 readonly property alias focusedSurface: decoratedWindow.focusedSurface
1155 readonly property bool dragging: touchControls.overlayShown ? touchControls.dragging : decoratedWindow.dragging
1157 readonly property string appId: model.application.appId
1158 readonly property alias clientAreaItem: decoratedWindow.clientAreaItem
1160 // It is Lomiri policy to close any window but the last one during OOM teardown
1163 target: model.window.surface
1165 if ((!surface.live && application && application.surfaceCount > 1) || !application)
1166 topLevelSurfaceList.removeAt(appRepeater.indexOf(appDelegate));
1171 function activate() {
1172 if (model.window.focused) {
1173 updateQmlFocusFromMirSurfaceFocus();
1176 // Activate the window since it has a surface (with a running app) backing it
1177 model.window.activate();
1179 // Otherwise, cause a respawn of the app, and trigger it's refocusing as the last window
1180 topLevelSurfaceList.raiseId(model.window.id);
1184 function requestMaximize() { model.window.requestState(Mir.MaximizedState); }
1185 function requestMaximizeVertically() { model.window.requestState(Mir.VertMaximizedState); }
1186 function requestMaximizeHorizontally() { model.window.requestState(Mir.HorizMaximizedState); }
1187 function requestMaximizeLeft() { model.window.requestState(Mir.MaximizedLeftState); }
1188 function requestMaximizeRight() { model.window.requestState(Mir.MaximizedRightState); }
1189 function requestMaximizeTopLeft() { model.window.requestState(Mir.MaximizedTopLeftState); }
1190 function requestMaximizeTopRight() { model.window.requestState(Mir.MaximizedTopRightState); }
1191 function requestMaximizeBottomLeft() { model.window.requestState(Mir.MaximizedBottomLeftState); }
1192 function requestMaximizeBottomRight() { model.window.requestState(Mir.MaximizedBottomRightState); }
1193 function requestMinimize() { model.window.requestState(Mir.MinimizedState); }
1194 function requestRestore() { model.window.requestState(Mir.RestoredState); }
1196 function claimFocus() {
1197 if (root.state == "spread") {
1198 spreadItem.highlightedIndex = index
1199 // force pendingActivation so that when switching to staged mode, topLevelSurfaceList focus won't got to previous app ( case when apps are launched from outside )
1200 topLevelSurfaceList.pendingActivation();
1201 priv.goneToSpread = false;
1203 if (root.mode == "stagedWithSideStage") {
1204 if (appDelegate.stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
1207 priv.updateMainAndSideStageIndexes();
1209 appDelegate.focus = true;
1211 // Don't set focusedAppDelegate (and signal mainAppChanged) unnecessarily
1212 // which can happen after getting interactive again.
1213 if (priv.focusedAppDelegate !== appDelegate)
1214 priv.focusedAppDelegate = appDelegate;
1217 function updateQmlFocusFromMirSurfaceFocus() {
1218 if (model.window.focused) {
1220 decoratedWindow.focus = true;
1225 id: windowStateSaver
1227 screenWidth: appContainer.width
1228 screenHeight: appContainer.height
1229 leftMargin: root.availableDesktopArea.x
1230 minimumY: root.availableDesktopArea.y
1234 target: model.window
1236 updateQmlFocusFromMirSurfaceFocus();
1237 if (!model.window.focused) {
1238 inhibitSlideAnimation = false;
1242 appDelegate.activate();
1245 if (value == Mir.MinimizedState) {
1246 appDelegate.minimize();
1247 } else if (value == Mir.MaximizedState) {
1248 appDelegate.maximize();
1249 } else if (value == Mir.VertMaximizedState) {
1250 appDelegate.maximizeVertically();
1251 } else if (value == Mir.HorizMaximizedState) {
1252 appDelegate.maximizeHorizontally();
1253 } else if (value == Mir.MaximizedLeftState) {
1254 appDelegate.maximizeLeft();
1255 } else if (value == Mir.MaximizedRightState) {
1256 appDelegate.maximizeRight();
1257 } else if (value == Mir.MaximizedTopLeftState) {
1258 appDelegate.maximizeTopLeft();
1259 } else if (value == Mir.MaximizedTopRightState) {
1260 appDelegate.maximizeTopRight();
1261 } else if (value == Mir.MaximizedBottomLeftState) {
1262 appDelegate.maximizeBottomLeft();
1263 } else if (value == Mir.MaximizedBottomRightState) {
1264 appDelegate.maximizeBottomRight();
1265 } else if (value == Mir.RestoredState) {
1266 if (appDelegate.fullscreen && appDelegate.prevWindowState != WindowStateStorage.WindowStateRestored
1267 && appDelegate.prevWindowState != WindowStateStorage.WindowStateNormal) {
1268 model.window.requestState(WindowStateStorage.toMirState(appDelegate.prevWindowState));
1270 appDelegate.restore();
1272 } else if (value == Mir.FullscreenState) {
1273 appDelegate.prevWindowState = appDelegate.windowState;
1274 appDelegate.windowState = WindowStateStorage.WindowStateFullscreen;
1279 readonly property bool windowReady: clientAreaItem.surfaceInitialized
1280 onWindowReadyChanged: {
1282 var loadedMirState = WindowStateStorage.toMirState(windowStateSaver.loadedState);
1283 var state = loadedMirState;
1285 if (window.state == Mir.FullscreenState) {
1286 // If the app is fullscreen at startup, we should not use saved state
1287 // Example of why: if you open game that only requests fullscreen at
1288 // Statup, this will automaticly be set to "restored state" since
1289 // thats the default value of stateStorage, this will result in the app
1290 // having the "restored state" as it will not make a fullscreen
1291 // call after the app has started.
1292 console.log("Initial window state is fullscreen, not using saved state.");
1293 state = window.state;
1294 } else if (loadedMirState == Mir.FullscreenState) {
1295 // If saved state is fullscreen, we should use app initial state
1296 // Example of why: if you open browser with youtube video at fullscreen
1297 // and close this app, it will be fullscreen next time you open the app.
1298 console.log("Saved window state is fullscreen, using initial window state");
1299 state = window.state;
1302 // need to apply the shell chrome policy on top the saved window state
1304 if (root.mode == "windowed") {
1305 policy = windowedFullscreenPolicy;
1307 policy = stagedFullscreenPolicy
1309 window.requestState(policy.applyPolicy(state, surface.shellChrome));
1313 Component.onCompleted: {
1314 if (application && application.rotatesWindowContents) {
1315 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
1317 decoratedWindow.surfaceOrientationAngle = 0;
1320 // First, cascade the newly created window, relative to the currently/old focused window.
1321 windowedX = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedX + units.gu(3) : (normalZ - 1) * units.gu(3)
1322 windowedY = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedY + units.gu(3) : normalZ * units.gu(3)
1323 // Now load any saved state. This needs to happen *after* the cascading!
1324 windowStateSaver.load();
1326 updateQmlFocusFromMirSurfaceFocus();
1329 _constructing = false;
1331 Component.onDestruction: {
1332 windowStateSaver.save();
1335 // This stage is about to be destroyed. Don't mess up with the model at this point
1339 if (visuallyMaximized) {
1340 priv.updateForegroundMaximizedApp();
1344 onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
1346 property bool _constructing: true;
1348 if (!_constructing) {
1349 priv.updateMainAndSideStageIndexes();
1355 && !greeter.fullyShown
1356 && (priv.foregroundMaximizedAppDelegate === null || priv.foregroundMaximizedAppDelegate.normalZ <= z)
1358 || appDelegate.fullscreen
1359 || focusAnimation.running || rightEdgeFocusAnimation.running || hidingAnimation.running
1362 model.window.close();
1365 function maximize(animated) {
1366 animationsEnabled = (animated === undefined) || animated;
1367 windowState = WindowStateStorage.WindowStateMaximized;
1369 function maximizeLeft(animated) {
1370 animationsEnabled = (animated === undefined) || animated;
1371 windowState = WindowStateStorage.WindowStateMaximizedLeft;
1373 function maximizeRight(animated) {
1374 animationsEnabled = (animated === undefined) || animated;
1375 windowState = WindowStateStorage.WindowStateMaximizedRight;
1377 function maximizeHorizontally(animated) {
1378 animationsEnabled = (animated === undefined) || animated;
1379 windowState = WindowStateStorage.WindowStateMaximizedHorizontally;
1381 function maximizeVertically(animated) {
1382 animationsEnabled = (animated === undefined) || animated;
1383 windowState = WindowStateStorage.WindowStateMaximizedVertically;
1385 function maximizeTopLeft(animated) {
1386 animationsEnabled = (animated === undefined) || animated;
1387 windowState = WindowStateStorage.WindowStateMaximizedTopLeft;
1389 function maximizeTopRight(animated) {
1390 animationsEnabled = (animated === undefined) || animated;
1391 windowState = WindowStateStorage.WindowStateMaximizedTopRight;
1393 function maximizeBottomLeft(animated) {
1394 animationsEnabled = (animated === undefined) || animated;
1395 windowState = WindowStateStorage.WindowStateMaximizedBottomLeft;
1397 function maximizeBottomRight(animated) {
1398 animationsEnabled = (animated === undefined) || animated;
1399 windowState = WindowStateStorage.WindowStateMaximizedBottomRight;
1401 function minimize(animated) {
1402 animationsEnabled = (animated === undefined) || animated;
1403 windowState |= WindowStateStorage.WindowStateMinimized; // add the minimized bit
1405 function restore(animated,state) {
1406 animationsEnabled = (animated === undefined) || animated;
1407 windowState = state || WindowStateStorage.WindowStateRestored;
1408 windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
1409 prevWindowState = windowState;
1412 function playFocusAnimation() {
1413 if (state == "stagedRightEdge") {
1414 // TODO: Can we drop this if and find something that always works?
1415 if (root.mode == "staged") {
1416 rightEdgeFocusAnimation.targetX = 0
1417 rightEdgeFocusAnimation.start()
1418 } else if (root.mode == "stagedWithSideStage") {
1419 rightEdgeFocusAnimation.targetX = appDelegate.stage == ApplicationInfoInterface.SideStage ? sideStage.x : 0
1420 rightEdgeFocusAnimation.start()
1422 } else if (state == "windowedRightEdge" || state == "windowed") {
1425 focusAnimation.start()
1428 function playHidingAnimation() {
1429 if (state != "windowedRightEdge") {
1430 hidingAnimation.start()
1434 function refreshStage() {
1435 var newStage = ApplicationInfoInterface.MainStage;
1436 if (priv.sideStageEnabled) { // we're in lanscape rotation.
1437 if (application && application.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {
1438 var defaultStage = ApplicationInfoInterface.SideStage; // if application supports portrait, it defaults to sidestage.
1439 if (application.supportedOrientations & (Qt.LandscapeOrientation|Qt.InvertedLandscapeOrientation)) {
1440 // if it supports lanscape, it defaults to mainstage.
1441 defaultStage = ApplicationInfoInterface.MainStage;
1443 newStage = WindowStateStorage.getStage(application.appId, defaultStage);
1448 if (focus && stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
1453 LomiriNumberAnimation {
1459 duration: LomiriAnimation.SnapDuration
1461 topLevelSurfaceList.pendingActivation();
1462 topLevelSurfaceList.raiseId(model.window.id);
1465 appDelegate.activate();
1469 id: rightEdgeFocusAnimation
1470 property int targetX: 0
1471 LomiriNumberAnimation { target: appDelegate; properties: "x"; to: rightEdgeFocusAnimation.targetX; duration: priv.animationDuration }
1472 LomiriNumberAnimation { target: decoratedWindow; properties: "angle"; to: 0; duration: priv.animationDuration }
1473 LomiriNumberAnimation { target: decoratedWindow; properties: "itemScale"; to: 1; duration: priv.animationDuration }
1475 topLevelSurfaceList.pendingActivation();
1476 inhibitSlideAnimation = true;
1479 appDelegate.activate();
1484 LomiriNumberAnimation { target: appDelegate; property: "opacity"; to: 0; duration: priv.animationDuration }
1485 onStopped: appDelegate.opacity = 1
1492 flickable: floatingFlickable
1496 sceneWidth: root.width
1497 stage: appDelegate.stage
1498 thisDelegate: appDelegate
1499 mainStageDelegate: priv.mainStageDelegate
1500 sideStageDelegate: priv.sideStageDelegate
1501 sideStageWidth: sideStage.panelWidth
1502 sideStageHandleWidth: sideStage.handleWidth
1503 sideStageX: sideStage.x
1504 itemIndex: appDelegate.itemIndex
1505 nextInStack: priv.nextInStack
1506 animationDuration: priv.animationDuration
1509 StagedRightEdgeMaths {
1510 id: stagedRightEdgeMaths
1511 sceneWidth: root.availableDesktopArea.width
1512 sceneHeight: appContainer.height
1513 isMainStageApp: priv.mainStageDelegate == appDelegate
1514 isSideStageApp: priv.sideStageDelegate == appDelegate
1515 sideStageWidth: sideStage.width
1516 sideStageOpen: sideStage.shown
1518 nextInStack: priv.nextInStack
1520 targetHeight: spreadItem.stackHeight
1521 targetX: spreadMaths.targetX
1522 startY: appDelegate.fullscreen ? 0 : root.availableDesktopArea.y
1523 targetY: spreadMaths.targetY
1524 targetAngle: spreadMaths.targetAngle
1525 targetScale: spreadMaths.targetScale
1526 shuffledZ: stageMaths.itemZ
1527 breakPoint: spreadItem.rightEdgeBreakPoint
1530 WindowedRightEdgeMaths {
1531 id: windowedRightEdgeMaths
1533 startWidth: appDelegate.requestedWidth
1534 startHeight: appDelegate.requestedHeight
1535 targetHeight: spreadItem.stackHeight
1536 targetX: spreadMaths.targetX
1537 targetY: spreadMaths.targetY
1538 normalZ: appDelegate.normalZ
1539 targetAngle: spreadMaths.targetAngle
1540 targetScale: spreadMaths.targetScale
1541 breakPoint: spreadItem.rightEdgeBreakPoint
1546 name: "spread"; when: root.state == "spread"
1547 StateChangeScript { script: { decoratedWindow.cancelDrag(); } }
1549 target: decoratedWindow;
1550 showDecoration: false;
1551 angle: spreadMaths.targetAngle
1552 itemScale: spreadMaths.targetScale
1553 scaleToPreviewSize: spreadItem.stackHeight
1554 scaleToPreviewProgress: 1
1555 hasDecoration: root.mode === "windowed"
1556 shadowOpacity: spreadMaths.shadowOpacity
1557 showHighlight: spreadItem.highlightedIndex === index
1558 darkening: spreadItem.highlightedIndex >= 0
1559 anchors.topMargin: dragArea.distance
1563 x: spreadMaths.targetX
1564 y: spreadMaths.targetY
1566 height: spreadItem.spreadItemHeight
1567 visible: spreadMaths.itemVisible
1569 PropertyChanges { target: dragArea; enabled: true }
1570 PropertyChanges { target: windowInfoItem; opacity: spreadMaths.tileInfoOpacity; visible: spreadMaths.itemVisible }
1571 PropertyChanges { target: touchControls; enabled: false }
1574 name: "stagedRightEdge"
1575 when: (root.mode == "staged" || root.mode == "stagedWithSideStage") && (root.state == "sideStagedRightEdge" || root.state == "stagedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running)
1577 target: stagedRightEdgeMaths
1578 progress: Math.max(rightEdgePushProgress, rightEdgeDragArea.draggedProgress)
1582 x: stagedRightEdgeMaths.animatedX
1583 y: stagedRightEdgeMaths.animatedY
1584 z: stagedRightEdgeMaths.animatedZ
1585 height: stagedRightEdgeMaths.animatedHeight
1586 visible: appDelegate.x < root.width
1589 target: decoratedWindow
1590 hasDecoration: false
1591 angle: stagedRightEdgeMaths.animatedAngle
1592 itemScale: stagedRightEdgeMaths.animatedScale
1593 scaleToPreviewSize: spreadItem.stackHeight
1594 scaleToPreviewProgress: stagedRightEdgeMaths.scaleToPreviewProgress
1597 // make sure it's visible but transparent so it fades in when we transition to spread
1598 PropertyChanges { target: windowInfoItem; opacity: 0; visible: true }
1601 name: "windowedRightEdge"
1602 when: root.mode == "windowed" && (root.state == "windowedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running || rightEdgePushProgress > 0)
1604 target: windowedRightEdgeMaths
1605 swipeProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0
1606 pushProgress: rightEdgePushProgress
1610 x: windowedRightEdgeMaths.animatedX
1611 y: windowedRightEdgeMaths.animatedY
1612 z: windowedRightEdgeMaths.animatedZ
1613 height: stagedRightEdgeMaths.animatedHeight
1616 target: decoratedWindow
1617 showDecoration: windowedRightEdgeMaths.decorationHeight
1618 angle: windowedRightEdgeMaths.animatedAngle
1619 itemScale: windowedRightEdgeMaths.animatedScale
1620 scaleToPreviewSize: spreadItem.stackHeight
1621 scaleToPreviewProgress: windowedRightEdgeMaths.scaleToPreviewProgress
1625 target: opacityEffect;
1626 opacityValue: windowedRightEdgeMaths.opacityMask
1627 sourceItem: windowedRightEdgeMaths.opacityMask < 1 ? decoratedWindow : null
1631 name: "staged"; when: root.state == "staged"
1635 y: root.availableDesktopArea.y
1636 visuallyMaximized: true
1637 visible: appDelegate.x < root.width
1641 requestedWidth: appContainer.width
1642 requestedHeight: root.availableDesktopArea.height
1643 restoreEntryValues: false
1646 target: decoratedWindow
1647 hasDecoration: false
1655 animateX: !focusAnimation.running && !rightEdgeFocusAnimation.running && itemIndex !== spreadItem.highlightedIndex && !inhibitSlideAnimation
1658 target: appDelegate.window
1659 allowClientResize: false
1663 name: "stagedWithSideStage"; when: root.state == "stagedWithSideStage"
1671 y: root.availableDesktopArea.y
1673 visuallyMaximized: true
1674 visible: appDelegate.x < root.width
1678 requestedWidth: stageMaths.itemWidth
1679 requestedHeight: root.availableDesktopArea.height
1680 restoreEntryValues: false
1683 target: decoratedWindow
1684 hasDecoration: false
1691 target: appDelegate.window
1692 allowClientResize: false
1696 name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
1698 target: appDelegate;
1699 requestedX: root.availableDesktopArea.x;
1701 visuallyMinimized: false;
1702 visuallyMaximized: true
1706 requestedWidth: root.availableDesktopArea.width;
1707 requestedHeight: appContainer.height;
1708 restoreEntryValues: false
1710 PropertyChanges { target: touchControls; enabled: true }
1711 PropertyChanges { target: decoratedWindow; windowControlButtonsVisible: false }
1714 name: "fullscreen"; when: appDelegate.fullscreen && !appDelegate.minimized
1716 target: appDelegate;
1722 requestedWidth: appContainer.width
1723 requestedHeight: appContainer.height
1724 restoreEntryValues: false
1726 PropertyChanges { target: decoratedWindow; hasDecoration: false }
1730 when: appDelegate.windowState == WindowStateStorage.WindowStateNormal
1733 visuallyMinimized: false
1735 PropertyChanges { target: touchControls; enabled: true }
1736 PropertyChanges { target: resizeArea; enabled: true }
1737 PropertyChanges { target: decoratedWindow; shadowOpacity: .3; windowControlButtonsVisible: true}
1740 requestedWidth: windowedWidth
1741 requestedHeight: windowedHeight
1742 restoreEntryValues: false
1747 when: appDelegate.windowState == WindowStateStorage.WindowStateRestored
1750 restoreEntryValues: false
1751 target: appDelegate;
1752 windowedX: restoredX;
1753 windowedY: restoredY;
1757 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
1761 windowedX: root.availableDesktopArea.x
1762 windowedY: root.availableDesktopArea.y
1763 windowedWidth: root.availableDesktopArea.width / 2
1764 windowedHeight: root.availableDesktopArea.height
1768 name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
1769 extend: "maximizedLeft"
1771 target: appDelegate;
1772 windowedX: root.availableDesktopArea.x + (root.availableDesktopArea.width / 2)
1776 name: "maximizedTopLeft"; when: appDelegate.maximizedTopLeft && !appDelegate.minimized
1780 windowedX: root.availableDesktopArea.x
1781 windowedY: root.availableDesktopArea.y
1782 windowedWidth: root.availableDesktopArea.width / 2
1783 windowedHeight: root.availableDesktopArea.height / 2
1787 name: "maximizedTopRight"; when: appDelegate.maximizedTopRight && !appDelegate.minimized
1788 extend: "maximizedTopLeft"
1791 windowedX: root.availableDesktopArea.x + (root.availableDesktopArea.width / 2)
1795 name: "maximizedBottomLeft"; when: appDelegate.maximizedBottomLeft && !appDelegate.minimized
1799 windowedX: root.availableDesktopArea.x
1800 windowedY: root.availableDesktopArea.y + (root.availableDesktopArea.height / 2)
1801 windowedWidth: root.availableDesktopArea.width / 2
1802 windowedHeight: root.availableDesktopArea.height / 2
1806 name: "maximizedBottomRight"; when: appDelegate.maximizedBottomRight && !appDelegate.minimized
1807 extend: "maximizedBottomLeft"
1810 windowedX: root.availableDesktopArea.x + (root.availableDesktopArea.width / 2)
1814 name: "maximizedHorizontally"; when: appDelegate.maximizedHorizontally && !appDelegate.minimized
1818 windowedX: root.availableDesktopArea.x; windowedY: windowedY
1819 windowedWidth: root.availableDesktopArea.width; windowedHeight: windowedHeight
1823 name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
1827 windowedX: windowedX; windowedY: root.availableDesktopArea.y
1828 windowedWidth: windowedWidth; windowedHeight: root.availableDesktopArea.height
1832 name: "minimized"; when: appDelegate.minimized
1835 scale: units.gu(5) / appDelegate.width
1837 visuallyMinimized: true
1838 visuallyMaximized: false
1839 x: -appDelegate.width / 2
1847 // These two animate applications into position from Staged to Desktop and back
1849 from: "staged,stagedWithSideStage"
1850 to: "normal,restored,maximized,maximizedHorizontally,maximizedVertically,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedBottomLeft,maximizedTopRight,maximizedBottomRight"
1851 enabled: appDelegate.animationsEnabled
1852 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
1853 LomiriNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,opacity,requestedWidth,requestedHeight,scale"; duration: priv.animationDuration }
1856 from: "normal,restored,maximized,maximizedHorizontally,maximizedVertically,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedBottomLeft,maximizedTopRight,maximizedBottomRight"
1857 to: "staged,stagedWithSideStage"
1858 LomiriNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,requestedWidth,requestedHeight"; duration: priv.animationDuration}
1862 from: "normal,restored,maximized,maximizedHorizontally,maximizedVertically,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedBottomLeft,maximizedTopRight,maximizedBottomRight,staged,stagedWithSideStage,windowedRightEdge,stagedRightEdge";
1864 // DecoratedWindow wants the scaleToPreviewSize set before enabling scaleToPreview
1865 PropertyAction { target: appDelegate; properties: "z,visible" }
1866 PropertyAction { target: decoratedWindow; property: "scaleToPreviewSize" }
1867 LomiriNumberAnimation { target: appDelegate; properties: "x,y,height"; duration: priv.animationDuration }
1868 LomiriNumberAnimation { target: decoratedWindow; properties: "width,height,itemScale,angle,scaleToPreviewProgress"; duration: priv.animationDuration }
1869 LomiriNumberAnimation { target: windowInfoItem; properties: "opacity"; duration: priv.animationDuration }
1872 from: "normal,staged"; to: "stagedWithSideStage"
1873 LomiriNumberAnimation { target: appDelegate; properties: "x,y,requestedWidth,requestedHeight"; duration: priv.animationDuration }
1876 to: "windowedRightEdge"
1879 windowedRightEdgeMaths.startX = appDelegate.requestedX
1880 windowedRightEdgeMaths.startY = appDelegate.requestedY
1883 var thisRect = { x: appDelegate.windowedX, y: appDelegate.windowedY, width: appDelegate.requestedWidth, height: appDelegate.requestedHeight }
1884 var otherDelegate = appRepeater.itemAt(0);
1885 var otherRect = { x: otherDelegate.windowedX, y: otherDelegate.windowedY, width: otherDelegate.requestedWidth, height: otherDelegate.requestedHeight }
1886 var intersectionRect = MathUtils.intersectionRect(thisRect, otherRect)
1887 var mappedInterSectionRect = appDelegate.mapFromItem(root, intersectionRect.x, intersectionRect.y)
1888 opacityEffect.maskX = mappedInterSectionRect.x
1889 opacityEffect.maskY = mappedInterSectionRect.y
1890 opacityEffect.maskWidth = intersectionRect.width
1891 opacityEffect.maskHeight = intersectionRect.height
1897 from: "stagedRightEdge"; to: "staged"
1898 enabled: rightEdgeDragArea.cancelled // only transition back to state if the gesture was cancelled, in the other cases we play the focusAnimations.
1899 SequentialAnimation {
1901 LomiriNumberAnimation { target: appDelegate; properties: "x,y,height,width,scale"; duration: priv.animationDuration }
1902 LomiriNumberAnimation { target: decoratedWindow; properties: "width,height,itemScale,angle,scaleToPreviewProgress"; duration: priv.animationDuration }
1904 // We need to release scaleToPreviewSize at last
1905 PropertyAction { target: decoratedWindow; property: "scaleToPreviewSize" }
1906 PropertyAction { target: appDelegate; property: "visible" }
1910 from: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1912 SequentialAnimation {
1913 ScriptAction { script: { fakeRectangle.stop(); } }
1914 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1915 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1916 LomiriNumberAnimation { target: appDelegate; properties: "x,y,scale,opacity"; duration: priv.animationDuration }
1917 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1922 to: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1923 SequentialAnimation {
1924 PropertyAction { target: appDelegate; property: "visuallyMinimized,z" }
1926 LomiriNumberAnimation { target: appDelegate; properties: "x"; from: -appDelegate.width / 2; duration: priv.animationDuration }
1927 LomiriNumberAnimation { target: appDelegate; properties: "y,opacity"; duration: priv.animationDuration }
1928 LomiriNumberAnimation { target: appDelegate; properties: "scale"; from: 0; duration: priv.animationDuration }
1930 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1934 id: windowedTransition
1935 from: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen,minimized"
1936 to: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1937 enabled: appDelegate.animationsEnabled
1938 SequentialAnimation {
1939 ScriptAction { script: {
1940 if (appDelegate.visuallyMaximized) visuallyMaximized = false; // maximized before -> going to restored
1943 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1944 LomiriNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,windowedX,windowedY,opacity,scale,requestedWidth,requestedHeight,windowedWidth,windowedHeight";
1945 duration: priv.animationDuration }
1946 ScriptAction { script: {
1947 fakeRectangle.stop();
1948 appDelegate.visuallyMaximized = appDelegate.maximized; // reflect the target state
1957 property: "decorationsAlwaysVisible"
1958 value: appDelegate && appDelegate.maximized && touchControls.overlayShown
1963 objectName: "windowResizeArea"
1965 anchors.fill: appDelegate
1967 // workaround so that it chooses the correct resize borders when you drag from a corner ResizeGrip
1968 anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
1971 boundsItem: root.availableDesktopArea
1972 minWidth: units.gu(10)
1973 minHeight: units.gu(10)
1974 borderThickness: units.gu(2)
1977 readyToAssesBounds: !appDelegate._constructing
1980 appDelegate.activate();
1986 objectName: "decoratedWindow"
1987 anchors.left: appDelegate.left
1988 anchors.top: appDelegate.top
1989 application: model.application
1990 surface: model.window.surface
1991 active: model.window.focused
1993 interactive: root.interactive
1995 decorationHeight: priv.windowDecorationHeight
1996 maximizeButtonShown: appDelegate.canBeMaximized
1997 overlayShown: touchControls.overlayShown
1998 width: implicitWidth
1999 height: implicitHeight
2000 highlightSize: windowInfoItem.iconMargin / 2
2001 boundsItem: root.availableDesktopArea
2002 panelState: root.panelState
2003 altDragEnabled: root.mode == "windowed"
2005 requestedWidth: appDelegate.requestedWidth
2006 requestedHeight: appDelegate.requestedHeight
2008 onCloseClicked: { appDelegate.close(); }
2009 onMaximizeClicked: {
2010 if (appDelegate.canBeMaximized) {
2011 appDelegate.anyMaximized ? appDelegate.requestRestore() : appDelegate.requestMaximize();
2014 onMaximizeHorizontallyClicked: {
2015 if (appDelegate.canBeMaximizedHorizontally) {
2016 appDelegate.maximizedHorizontally ? appDelegate.requestRestore() : appDelegate.requestMaximizeHorizontally()
2019 onMaximizeVerticallyClicked: {
2020 if (appDelegate.canBeMaximizedVertically) {
2021 appDelegate.maximizedVertically ? appDelegate.requestRestore() : appDelegate.requestMaximizeVertically()
2024 onMinimizeClicked: { appDelegate.requestMinimize(); }
2025 onDecorationPressed: { appDelegate.activate(); }
2026 onDecorationReleased: fakeRectangle.visible ? fakeRectangle.commit() : appDelegate.updateRestoredGeometry()
2028 property real angle: 0
2029 Behavior on angle { enabled: priv.closingIndex >= 0; LomiriNumberAnimation {} }
2030 property real itemScale: 1
2031 Behavior on itemScale { enabled: priv.closingIndex >= 0; LomiriNumberAnimation {} }
2036 origin.y: decoratedWindow.implicitHeight / 2
2037 xScale: decoratedWindow.itemScale
2038 yScale: decoratedWindow.itemScale
2041 origin { x: 0; y: (decoratedWindow.height / 2) }
2042 axis { x: 0; y: 1; z: 0 }
2043 angle: decoratedWindow.angle
2050 anchors.fill: decoratedWindow
2053 WindowControlsOverlay {
2055 anchors.fill: appDelegate
2057 resizeArea: resizeArea
2060 boundsItem: root.availableDesktopArea
2062 onFakeMaximizeAnimationRequested: if (!appDelegate.maximized) fakeRectangle.maximize(amount, true)
2063 onFakeMaximizeLeftAnimationRequested: if (!appDelegate.maximizedLeft) fakeRectangle.maximizeLeft(amount, true)
2064 onFakeMaximizeRightAnimationRequested: if (!appDelegate.maximizedRight) fakeRectangle.maximizeRight(amount, true)
2065 onFakeMaximizeTopLeftAnimationRequested: if (!appDelegate.maximizedTopLeft) fakeRectangle.maximizeTopLeft(amount, true);
2066 onFakeMaximizeTopRightAnimationRequested: if (!appDelegate.maximizedTopRight) fakeRectangle.maximizeTopRight(amount, true);
2067 onFakeMaximizeBottomLeftAnimationRequested: if (!appDelegate.maximizedBottomLeft) fakeRectangle.maximizeBottomLeft(amount, true);
2068 onFakeMaximizeBottomRightAnimationRequested: if (!appDelegate.maximizedBottomRight) fakeRectangle.maximizeBottomRight(amount, true);
2069 onStopFakeAnimation: fakeRectangle.stop();
2070 onDragReleased: fakeRectangle.visible ? fakeRectangle.commit() : appDelegate.updateRestoredGeometry()
2073 WindowedFullscreenPolicy {
2074 id: windowedFullscreenPolicy
2076 StagedFullscreenPolicy {
2077 id: stagedFullscreenPolicy
2078 active: root.mode == "staged" || root.mode == "stagedWithSideStage"
2079 surface: model.window.surface
2082 SpreadDelegateInputArea {
2084 objectName: "dragArea"
2085 anchors.fill: decoratedWindow
2089 dragDelegate: fakeDragItem
2092 spreadItem.highlightedIndex = index;
2093 if (distance == 0) {
2094 priv.goneToSpread = false;
2098 priv.closingIndex = index
2099 appDelegate.close();
2105 objectName: "windowInfoItem"
2106 anchors { left: parent.left; top: decoratedWindow.bottom; topMargin: units.gu(1) }
2107 title: model.application.name
2108 iconSource: model.application.icon
2109 height: spreadItem.appInfoHeight
2112 visible: opacity > 0
2114 var nextApp = appRepeater.itemAt(index + 1);
2116 return Math.max(iconHeight, nextApp.x - appDelegate.x - units.gu(1))
2118 return appDelegate.width;
2122 spreadItem.highlightedIndex = index;
2123 priv.goneToSpread = false;
2129 objectName: "closeMouseArea"
2130 anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset }
2131 readonly property var mousePos: hoverMouseArea.mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
2132 readonly property bool shown: dragArea.distance == 0
2133 && index == spreadItem.highlightedIndex
2134 && mousePos.y < (decoratedWindow.height / 3)
2135 && mousePos.y > -units.gu(4)
2136 && mousePos.x > -units.gu(4)
2137 && mousePos.x < (decoratedWindow.width * 2 / 3)
2138 opacity: shown ? 1 : 0
2139 visible: opacity > 0
2140 Behavior on opacity { LomiriNumberAnimation { duration: LomiriAnimation.SnapDuration } }
2145 priv.closingIndex = index;
2146 appDelegate.close();
2150 source: "graphics/window-close.svg"
2151 anchors.fill: closeMouseArea
2152 anchors.margins: units.gu(2)
2153 sourceSize.width: width
2154 sourceSize.height: height
2159 // Group all child windows in this item so that we can fade them out together when going to the spread
2160 // (and fade them in back again when returning from it)
2161 readonly property bool stageOnProperState: root.state === "windowed"
2162 || root.state === "staged"
2163 || root.state === "stagedWithSideStage"
2165 // TODO: Is it worth the extra cost of layering to avoid the opacity artifacts of intersecting children?
2166 // Btw, will involve more than uncommenting the line below as children won't necessarily fit this item's
2167 // geometry. This is just a reference.
2168 //layer.enabled: opacity !== 0.0 && opacity !== 1.0
2170 opacity: stageOnProperState ? 1.0 : 0.0
2171 visible: opacity !== 0.0 // make it transparent to input as well
2172 Behavior on opacity { LomiriNumberAnimation {} }
2175 id: childWindowRepeater
2176 model: appDelegate.surface ? appDelegate.surface.childSurfaceList : null
2178 delegate: ChildWindowTree {
2179 surface: model.surface
2181 // Account for the displacement caused by window decoration in the top-level surface
2182 // Ie, the top-level surface is not positioned at (0,0) of this ChildWindow's parent (appDelegate)
2183 displacementX: appDelegate.clientAreaItem.x
2184 displacementY: appDelegate.clientAreaItem.y
2186 boundsItem: root.availableDesktopArea
2187 decorationHeight: priv.windowDecorationHeight
2189 z: childWindowRepeater.count - model.index
2193 // some child surface in this tree got focus.
2194 // Ensure we also have it at the top-level hierarchy
2195 appDelegate.claimFocus();
2205 FakeMaximizeDelegate {
2207 target: priv.focusedAppDelegate
2208 leftMargin: root.availableDesktopArea.x
2209 appContainerWidth: appContainer.width
2210 appContainerHeight: appContainer.height
2211 panelState: root.panelState
2215 id: workspaceSwitcher
2216 enabled: workspaceEnabled
2217 anchors.centerIn: parent
2218 height: units.gu(20)
2219 width: root.width - units.gu(8)
2220 background: root.background
2223 appContainer.focus = true;
2229 id: shortRightEdgeSwipeAnimation
2232 duration: priv.animationDuration
2236 id: rightEdgeDragArea
2237 objectName: "rightEdgeDragArea"
2238 direction: Direction.Leftwards
2239 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
2240 width: root.dragAreaWidth
2241 enabled: root.spreadEnabled
2243 property var gesturePoints: []
2244 property bool cancelled: false
2246 property real progress: -touchPosition.x / root.width
2247 onProgressChanged: {
2249 draggedProgress = progress;
2253 property real draggedProgress: 0
2255 onTouchPositionChanged: {
2256 gesturePoints.push(touchPosition.x);
2257 if (gesturePoints.length > 10) {
2258 gesturePoints.splice(0, gesturePoints.length - 10)
2262 onDraggingChanged: {
2264 // A potential edge-drag gesture has started. Start recording it
2267 draggedProgress = 0;
2269 // Ok. The user released. Did he drag far enough to go to full spread?
2270 if (gesturePoints[gesturePoints.length - 1] < -spreadItem.rightEdgeBreakPoint * spreadItem.width ) {
2272 // He dragged far enough, but if the last movement was a flick to the right again, he wants to cancel the spread again.
2273 var oneWayFlickToRight = true;
2274 var smallestX = gesturePoints[0]-1;
2275 for (var i = 0; i < gesturePoints.length; i++) {
2276 if (gesturePoints[i] <= smallestX) {
2277 oneWayFlickToRight = false;
2280 smallestX = gesturePoints[i];
2283 if (!oneWayFlickToRight) {
2284 // Ok, the user made it, let's go to spread!
2285 priv.goneToSpread = true;
2290 // Ok, the user didn't drag far enough to cross the breakPoint
2291 // Find out if it was a one-way movement to the left, in which case we just switch directly to next app.
2292 var oneWayFlick = true;
2293 var smallestX = rightEdgeDragArea.width;
2294 for (var i = 0; i < gesturePoints.length; i++) {
2295 if (gesturePoints[i] >= smallestX) {
2296 oneWayFlick = false;
2299 smallestX = gesturePoints[i];
2302 if (appRepeater.count > 1 &&
2303 (oneWayFlick && rightEdgeDragArea.distance > units.gu(2) || rightEdgeDragArea.distance > spreadItem.rightEdgeBreakPoint * spreadItem.width)) {
2304 var nextStage = appRepeater.itemAt(priv.nextInStack).stage
2305 for (var i = 0; i < appRepeater.count; i++) {
2306 if (i != priv.nextInStack && appRepeater.itemAt(i).stage == nextStage) {
2307 appRepeater.itemAt(i).playHidingAnimation()
2311 appRepeater.itemAt(priv.nextInStack).playFocusAnimation()
2312 if (appRepeater.itemAt(priv.nextInStack).stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
2326 TabletSideStageTouchGesture {
2328 objectName: "triGestureArea"
2329 anchors.fill: parent
2331 property Item appDelegate
2333 dragComponent: dragComponent
2334 dragComponentProperties: { "appDelegate": appDelegate }
2337 function matchDelegate(obj) { return String(obj.objectName).indexOf("appDelegate") >= 0; }
2339 var delegateAtCenter = Functions.itemAt(appContainer, x, y, matchDelegate);
2340 if (!delegateAtCenter) return;
2342 appDelegate = delegateAtCenter;
2346 if (sideStage.shown) {
2350 priv.updateMainAndSideStageIndexes()
2355 // If we're dragging to the sidestage.
2356 if (!sideStage.shown) {
2364 property Item appDelegate
2366 surface: appDelegate ? appDelegate.surface : null
2368 consumesInput: false
2371 requestedWidth: appDelegate ? appDelegate.requestedWidth : 0
2372 requestedHeight: appDelegate ? appDelegate.requestedHeight : 0
2375 height: units.gu(40)
2377 Drag.hotSpot.x: width/2
2378 Drag.hotSpot.y: height/2
2379 // only accept opposite stage.
2381 if (!surface) return "Disabled";
2383 if (appDelegate.stage === ApplicationInfo.MainStage) {
2384 if (appDelegate.application.supportedOrientations
2385 & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {