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/>.
19 import QtQuick.Window 2.2
20 import Lomiri.Components 1.3
21 import QtMir.Application 0.1
22 import "../Components/PanelState"
23 import "../Components"
25 import Lomiri.Gestures 0.1
26 import GlobalShortcut 1.0
29 import "Spread/MathUtils.js" as MathUtils
30 import WindowManager 1.0
36 property QtObject applicationManager
37 property QtObject topLevelSurfaceList
38 property bool altTabPressed
39 property url background
40 property alias backgroundSourceSize: wallpaper.sourceSize
41 property int dragAreaWidth
42 property real nativeHeight
43 property real nativeWidth
44 property QtObject orientations
45 property int shellOrientation
46 property int shellOrientationAngle
47 property bool spreadEnabled: true // If false, animations and right edge will be disabled
48 property bool suspended
49 property bool oskEnabled: false
50 property rect inputMethodRect
51 property real rightEdgePushProgress: 0
52 property Item availableDesktopArea
53 property PanelState panelState
55 // Whether outside forces say that the Stage may have focus
56 property bool allowInteractivity
58 readonly property bool interactive: (state === "staged" || state === "stagedWithSideStage" || state === "windowed") && allowInteractivity
61 property string mode: "staged"
63 readonly property var temporarySelectedWorkspace: state == "spread" ? screensAndWorkspaces.activeWorkspace : null
64 property bool workspaceEnabled: (mode == "windowed" || settings.forceEnableWorkspace) && settings.enableWorkspace
66 // Used by the tutorial code
67 readonly property real rightEdgeDragProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0 // How far left the stage has been dragged
69 // used by the snap windows (edge maximize) feature
70 readonly property alias previewRectangle: fakeRectangle
72 readonly property bool spreadShown: state == "spread"
73 readonly property var mainApp: priv.focusedAppDelegate ? priv.focusedAppDelegate.application : null
75 // application windows never rotate independently
76 property int mainAppWindowOrientationAngle: shellOrientationAngle
78 property bool orientationChangesEnabled: !priv.focusedAppDelegate || priv.focusedAppDelegate.orientationChangesEnabled
80 property int supportedOrientations: {
84 return mainApp.supportedOrientations;
85 case "stagedWithSideStage":
86 var orientations = mainApp.supportedOrientations;
87 orientations |= Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation;
88 if (priv.sideStageItemId) {
89 // If we have a sidestage app, support Portrait orientation
90 // so that it will switch the sidestage app to mainstage on rotate to portrait
91 orientations |= Qt.PortraitOrientation|Qt.InvertedPortraitOrientation;
97 return Qt.PortraitOrientation |
98 Qt.LandscapeOrientation |
99 Qt.InvertedPortraitOrientation |
100 Qt.InvertedLandscapeOrientation;
105 schema.id: "com.lomiri.Shell"
108 property int launcherLeftMargin : 0
111 target: topLevelSurfaceList
112 property: "rootFocus"
116 onInteractiveChanged: {
117 // Stage must have focus before activating windows, including null
123 onAltTabPressedChanged: {
126 if (root.spreadEnabled) {
127 altTabDelayTimer.start();
130 // Alt Tab has been released, did we already go to spread?
131 if (priv.goneToSpread) {
132 priv.goneToSpread = false;
134 // No we didn't, do a quick alt-tab
135 if (appRepeater.count > 1) {
136 appRepeater.itemAt(1).activate();
137 } else if (appRepeater.count > 0) {
138 appRepeater.itemAt(0).activate(); // quick alt-tab to the only (minimized) window should still activate it
149 if (root.altTabPressed) {
150 priv.goneToSpread = true;
155 // For MirAL window management
157 normal: Qt.rect(0, root.mode === "windowed" ? priv.windowDecorationHeight : 0, 0, 0)
161 property Item itemConfiningMouseCursor: !spreadShown && priv.focusedAppDelegate && priv.focusedAppDelegate.window.confinesMousePointer ?
162 priv.focusedAppDelegate.clientAreaItem : null;
164 signal itemSnapshotRequested(Item item)
166 // functions to be called from outside
167 function updateFocusedAppOrientation() { /* TODO */ }
168 function updateFocusedAppOrientationAnimated() { /* TODO */}
170 function closeSpread() {
171 spreadItem.highlightedIndex = -1;
172 priv.goneToSpread = false;
175 onSpreadEnabledChanged: {
176 if (!spreadEnabled && spreadShown) {
181 onRightEdgePushProgressChanged: {
182 if (spreadEnabled && rightEdgePushProgress >= 1) {
183 priv.goneToSpread = true
188 id: lifecycleExceptions
189 schema.id: "com.canonical.qtmir"
192 function isExemptFromLifecycle(appId) {
193 var shortAppId = appId.split('_')[0];
194 for (var i = 0; i < lifecycleExceptions.lifecycleExemptAppids.length; i++) {
195 if (shortAppId === lifecycleExceptions.lifecycleExemptAppids[i]) {
203 id: closeFocusedShortcut
204 shortcut: Qt.AltModifier|Qt.Key_F4
206 if (priv.focusedAppDelegate) {
207 priv.focusedAppDelegate.close();
213 id: showSpreadShortcut
214 shortcut: Qt.MetaModifier|Qt.Key_W
215 active: root.spreadEnabled
216 onTriggered: priv.goneToSpread = true
220 id: minimizeAllShortcut
221 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_D
222 onTriggered: priv.minimizeAllWindows()
223 active: root.state == "windowed"
227 id: maximizeWindowShortcut
228 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up
229 onTriggered: priv.focusedAppDelegate.requestMaximize()
230 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximized
234 id: maximizeWindowLeftShortcut
235 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left
236 onTriggered: priv.focusedAppDelegate.requestMaximizeLeft()
237 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight
241 id: maximizeWindowRightShortcut
242 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right
243 onTriggered: priv.focusedAppDelegate.requestMaximizeRight()
244 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight
248 id: minimizeRestoreShortcut
249 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
251 if (priv.focusedAppDelegate.anyMaximized) {
252 priv.focusedAppDelegate.requestRestore();
254 priv.focusedAppDelegate.requestMinimize();
257 active: root.state == "windowed" && priv.focusedAppDelegate
261 shortcut: Qt.AltModifier|Qt.Key_Print
262 onTriggered: root.itemSnapshotRequested(priv.focusedAppDelegate)
263 active: priv.focusedAppDelegate !== null
267 shortcut: Qt.ControlModifier|Qt.AltModifier|Qt.Key_T
269 // try in this order: snap pkg, new deb name, old deb name
270 var candidates = ["lomiri-terminal-app_lomiri-terminal-app", "lomiri-terminal-app", "com.lomiri.terminal_terminal"];
271 for (var i = 0; i < candidates.length; i++) {
272 if (priv.startApp(candidates[i]))
279 id: showWorkspaceSwitcherShortcutLeft
280 shortcut: Qt.AltModifier|Qt.ControlModifier|Qt.Key_Left
281 active: !workspaceSwitcher.active
284 workspaceSwitcher.showLeft()
288 id: showWorkspaceSwitcherShortcutRight
289 shortcut: Qt.AltModifier|Qt.ControlModifier|Qt.Key_Right
290 active: !workspaceSwitcher.active
293 workspaceSwitcher.showRight()
297 id: showWorkspaceSwitcherShortcutUp
298 shortcut: Qt.AltModifier|Qt.ControlModifier|Qt.Key_Up
299 active: !workspaceSwitcher.active
302 workspaceSwitcher.showUp()
306 id: showWorkspaceSwitcherShortcutDown
307 shortcut: Qt.AltModifier|Qt.ControlModifier|Qt.Key_Down
308 active: !workspaceSwitcher.active
311 workspaceSwitcher.showDown()
317 objectName: "DesktopStagePrivate"
319 function startApp(appId) {
320 if (root.applicationManager.findApplication(appId)) {
321 return root.applicationManager.requestFocusApplication(appId);
323 return root.applicationManager.startApplication(appId) !== null;
327 property var focusedAppDelegate: null
328 property var foregroundMaximizedAppDelegate: null // for stuff like drop shadow and focusing maximized app by clicking panel
330 property bool goneToSpread: false
331 property int closingIndex: -1
332 property int animationDuration: LomiriAnimation.FastDuration
334 function updateForegroundMaximizedApp() {
336 for (var i = 0; i < appRepeater.count && !found; i++) {
337 var item = appRepeater.itemAt(i);
338 if (item && item.visuallyMaximized) {
339 foregroundMaximizedAppDelegate = item;
344 foregroundMaximizedAppDelegate = null;
348 function minimizeAllWindows() {
349 for (var i = appRepeater.count - 1; i >= 0; i--) {
350 var appDelegate = appRepeater.itemAt(i);
351 if (appDelegate && !appDelegate.minimized) {
352 appDelegate.requestMinimize();
357 readonly property bool sideStageEnabled: root.mode === "stagedWithSideStage" &&
358 (root.shellOrientation == Qt.LandscapeOrientation ||
359 root.shellOrientation == Qt.InvertedLandscapeOrientation)
360 onSideStageEnabledChanged: {
361 for (var i = 0; i < appRepeater.count; i++) {
362 appRepeater.itemAt(i).refreshStage();
364 priv.updateMainAndSideStageIndexes();
367 property var mainStageDelegate: null
368 property var sideStageDelegate: null
369 property int mainStageItemId: 0
370 property int sideStageItemId: 0
371 property string mainStageAppId: ""
372 property string sideStageAppId: ""
374 onSideStageDelegateChanged: {
375 if (!sideStageDelegate) {
380 function updateMainAndSideStageIndexes() {
381 if (root.mode != "stagedWithSideStage") {
382 priv.sideStageDelegate = null;
383 priv.sideStageItemId = 0;
384 priv.sideStageAppId = "";
385 priv.mainStageDelegate = appRepeater.itemAt(0);
386 priv.mainStageItemId = topLevelSurfaceList.idAt(0);
387 priv.mainStageAppId = topLevelSurfaceList.applicationAt(0) ? topLevelSurfaceList.applicationAt(0).appId : ""
391 var choseMainStage = false;
392 var choseSideStage = false;
394 if (!root.topLevelSurfaceList)
397 for (var i = 0; i < appRepeater.count && (!choseMainStage || !choseSideStage); ++i) {
398 var appDelegate = appRepeater.itemAt(i);
400 // This might happen during startup phase... If the delegate appears and claims focus
401 // things are updated and appRepeater.itemAt(x) still returns null while appRepeater.count >= x
402 // Lets just skip it, on startup it will be generated at a later point too...
405 if (sideStage.shown && appDelegate.stage == ApplicationInfoInterface.SideStage
406 && !choseSideStage) {
407 priv.sideStageDelegate = appDelegate
408 priv.sideStageItemId = root.topLevelSurfaceList.idAt(i);
409 priv.sideStageAppId = root.topLevelSurfaceList.applicationAt(i).appId;
410 choseSideStage = true;
411 } else if (!choseMainStage && appDelegate.stage == ApplicationInfoInterface.MainStage) {
412 priv.mainStageDelegate = appDelegate;
413 priv.mainStageItemId = root.topLevelSurfaceList.idAt(i);
414 priv.mainStageAppId = root.topLevelSurfaceList.applicationAt(i).appId;
415 choseMainStage = true;
418 if (!choseMainStage && priv.mainStageDelegate) {
419 priv.mainStageDelegate = null;
420 priv.mainStageItemId = 0;
421 priv.mainStageAppId = "";
423 if (!choseSideStage && priv.sideStageDelegate) {
424 priv.sideStageDelegate = null;
425 priv.sideStageItemId = 0;
426 priv.sideStageAppId = "";
430 property int nextInStack: {
431 var mainStageIndex = priv.mainStageDelegate ? priv.mainStageDelegate.itemIndex : -1;
432 var sideStageIndex = priv.sideStageDelegate ? priv.sideStageDelegate.itemIndex : -1;
433 if (sideStageIndex == -1) {
434 return topLevelSurfaceList.count > 1 ? 1 : -1;
436 if (mainStageIndex == 0 || sideStageIndex == 0) {
437 if (mainStageIndex == 1 || sideStageIndex == 1) {
438 return topLevelSurfaceList.count > 2 ? 2 : -1;
445 readonly property real virtualKeyboardHeight: root.inputMethodRect.height
447 readonly property real windowDecorationHeight: units.gu(3)
450 Component.onCompleted: priv.updateMainAndSideStageIndexes()
454 onCloseClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
455 onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestMinimize(); } }
456 onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestRestore(); } }
461 property: "decorationsVisible"
462 value: mode == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.maximized && !root.spreadShown
469 if (priv.focusedAppDelegate !== null) {
470 if (priv.focusedAppDelegate.maximized)
471 return priv.focusedAppDelegate.title
473 return priv.focusedAppDelegate.appName
477 when: priv.focusedAppDelegate
482 property: "focusedPersistentSurfaceId"
484 if (priv.focusedAppDelegate !== null) {
485 if (priv.focusedAppDelegate.surface) {
486 return priv.focusedAppDelegate.surface.persistentId;
491 when: priv.focusedAppDelegate
496 property: "dropShadow"
497 value: priv.focusedAppDelegate && !priv.focusedAppDelegate.maximized && priv.foregroundMaximizedAppDelegate !== null && mode == "windowed"
502 property: "closeButtonShown"
503 value: priv.focusedAppDelegate && priv.focusedAppDelegate.maximized
506 Component.onDestruction: {
507 panelState.title = "";
508 panelState.decorationsVisible = false;
509 panelState.dropShadow = false;
513 model: root.applicationManager
515 property var stateBinding: Binding {
516 target: model.application
517 property: "requestedState"
519 // TODO: figure out some lifecycle policy, like suspending minimized apps
520 // or something if running windowed.
521 // TODO: If the device has a dozen suspended apps because it was running
522 // in staged mode, when it switches to Windowed mode it will suddenly
523 // resume all those apps at once. We might want to avoid that.
524 value: root.mode === "windowed"
525 || (!root.suspended && model.application && priv.focusedAppDelegate &&
526 (priv.focusedAppDelegate.appId === model.application.appId ||
527 priv.mainStageAppId === model.application.appId ||
528 priv.sideStageAppId === model.application.appId))
529 ? ApplicationInfoInterface.RequestedRunning
530 : ApplicationInfoInterface.RequestedSuspended
533 property var lifecycleBinding: Binding {
534 target: model.application
535 property: "exemptFromLifecycle"
536 value: model.application
537 ? (!model.application.isTouchApp || isExemptFromLifecycle(model.application.appId))
541 property var focusRequestedConnection: Connections {
542 target: model.application
545 // Application emits focusRequested when it has no surface (i.e. their processes died).
546 // Find the topmost window for this application and activate it, after which the app
547 // will be requested to be running.
549 for (var i = 0; i < appRepeater.count; i++) {
550 var appDelegate = appRepeater.itemAt(i);
551 if (appDelegate.application.appId === model.application.appId) {
552 appDelegate.activate();
557 console.warn("Application requested te be focused but no window for it. What should we do?");
565 name: "spread"; when: priv.goneToSpread
566 PropertyChanges { target: floatingFlickable; enabled: true }
567 PropertyChanges { target: root; focus: true }
568 PropertyChanges { target: spreadItem; focus: true }
569 PropertyChanges { target: hoverMouseArea; enabled: true }
570 PropertyChanges { target: rightEdgeDragArea; enabled: false }
571 PropertyChanges { target: cancelSpreadMouseArea; enabled: true }
572 PropertyChanges { target: noAppsRunningHint; visible: (root.topLevelSurfaceList.count < 1) }
573 PropertyChanges { target: blurLayer; visible: true; blurRadius: 32; brightness: .65; opacity: 1 }
574 PropertyChanges { target: wallpaper; visible: false }
575 PropertyChanges { target: screensAndWorkspaces; opacity: 1 }
578 name: "stagedRightEdge"; when: root.spreadEnabled && (rightEdgeDragArea.dragging || rightEdgePushProgress > 0) && root.mode == "staged"
586 PropertyChanges { target: noAppsRunningHint; visible: (root.topLevelSurfaceList.count < 1) }
589 name: "sideStagedRightEdge"; when: root.spreadEnabled && (rightEdgeDragArea.dragging || rightEdgePushProgress > 0) && root.mode == "stagedWithSideStage"
590 extend: "stagedRightEdge"
593 opacity: priv.sideStageDelegate && priv.sideStageDelegate.x === sideStage.x ? 1 : 0
598 name: "windowedRightEdge"; when: root.spreadEnabled && (rightEdgeDragArea.dragging || rightEdgePushProgress > 0) && root.mode == "windowed"
604 opacity: MathUtils.linearAnimation(spreadItem.rightEdgeBreakPoint, 1, 0, 1, Math.max(rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0, rightEdgePushProgress))
608 name: "staged"; when: root.mode === "staged"
609 PropertyChanges { target: wallpaper; visible: !priv.focusedAppDelegate || priv.focusedAppDelegate.x !== 0 }
610 PropertyChanges { target: root; focus: true }
611 PropertyChanges { target: appContainer; focus: true }
614 name: "stagedWithSideStage"; when: root.mode === "stagedWithSideStage"
615 PropertyChanges { target: triGestureArea; enabled: priv.sideStageEnabled }
616 PropertyChanges { target: sideStage; visible: true }
617 PropertyChanges { target: root; focus: true }
618 PropertyChanges { target: appContainer; focus: true }
621 name: "windowed"; when: root.mode === "windowed"
622 PropertyChanges { target: root; focus: true }
623 PropertyChanges { target: appContainer; focus: true }
628 from: "stagedRightEdge,sideStagedRightEdge,windowedRightEdge"; to: "spread"
629 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
630 PropertyAction { target: screensAndWorkspaces; property: "activeWorkspace"; value: WMScreen.currentWorkspace }
631 PropertyAnimation { target: blurLayer; properties: "brightness,blurRadius"; duration: priv.animationDuration }
632 LomiriNumberAnimation { target: screensAndWorkspaces; property: "opacity"; duration: priv.animationDuration }
636 PropertyAction { target: screensAndWorkspaces; property: "activeWorkspace"; value: WMScreen.currentWorkspace }
637 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: appRepeater.count > 1 ? 1 : 0 }
638 PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
639 LomiriNumberAnimation { target: screensAndWorkspaces; property: "opacity"; duration: priv.animationDuration }
643 SequentialAnimation {
646 var item = appRepeater.itemAt(Math.max(0, spreadItem.highlightedIndex));
647 if (item.stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
650 item.playFocusAnimation();
653 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
654 PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
658 to: "stagedRightEdge,sideStagedRightEdge"
659 PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
662 to: "stagedWithSideStage"
663 ScriptAction { script: priv.updateMainAndSideStageIndexes(); }
669 id: cancelSpreadMouseArea
672 onClicked: priv.goneToSpread = false
677 objectName: "appContainer"
683 objectName: "stageBackground"
685 source: root.background
686 // Make sure it's the lowest item. Due to the left edge drag we sometimes need
687 // to put the dash at -1 and we don't want it behind the Wallpaper
698 ScreensAndWorkspaces {
699 id: screensAndWorkspaces
700 anchors { left: parent.left; top: parent.top; right: parent.right; leftMargin: root.leftMargin }
701 height: Math.max(units.gu(30), parent.height * .3)
702 background: root.background
704 visible: workspaceEnabled ? opacity > 0 : false
705 enabled: workspaceEnabled
706 onCloseSpread: priv.goneToSpread = false;
711 objectName: "spreadItem"
714 bottom: parent.bottom;
716 top: workspaceEnabled ? screensAndWorkspaces.bottom : parent.top;
718 leftMargin: root.availableDesktopArea.x
719 model: root.topLevelSurfaceList
720 spreadFlickable: floatingFlickable
724 priv.goneToSpread = false;
728 appRepeater.itemAt(highlightedIndex).close();
732 id: floatingFlickable
733 objectName: "spreadFlickable"
736 contentWidth: spreadItem.spreadTotalWidth
738 function snap(toIndex) {
739 var delegate = appRepeater.itemAt(toIndex)
740 var targetContentX = floatingFlickable.contentWidth / spreadItem.totalItemCount * toIndex;
741 if (targetContentX - floatingFlickable.contentX > spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) {
742 var offset = (spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) - (targetContentX - floatingFlickable.contentX)
743 snapAnimation.to = floatingFlickable.contentX - offset;
744 snapAnimation.start();
745 } else if (targetContentX - floatingFlickable.contentX < spreadItem.leftStackXPos + units.gu(1)) {
746 var offset = (spreadItem.leftStackXPos + units.gu(1)) - (targetContentX - floatingFlickable.contentX);
747 snapAnimation.to = floatingFlickable.contentX - offset;
748 snapAnimation.start();
751 LomiriNumberAnimation {id: snapAnimation; target: floatingFlickable; property: "contentX"}
756 objectName: "hoverMouseArea"
758 propagateComposedEvents: true
762 property bool wasTouchPress: false
764 property int scrollAreaWidth: width / 3
765 property bool progressiveScrollingEnabled: false
768 mouse.accepted = false
770 if (hoverMouseArea.pressed || wasTouchPress) {
774 // Find the hovered item and mark it active
775 for (var i = appRepeater.count - 1; i >= 0; i--) {
776 var appDelegate = appRepeater.itemAt(i);
777 var mapped = mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
778 var itemUnder = appDelegate.childAt(mapped.x, mapped.y);
779 if (itemUnder && (itemUnder.objectName === "dragArea" || itemUnder.objectName === "windowInfoItem" || itemUnder.objectName == "closeMouseArea")) {
780 spreadItem.highlightedIndex = i;
785 if (floatingFlickable.contentWidth > floatingFlickable.width) {
786 var margins = floatingFlickable.width * 0.05;
788 if (!progressiveScrollingEnabled && mouseX < floatingFlickable.width - scrollAreaWidth) {
789 progressiveScrollingEnabled = true
792 // do we need to scroll?
793 if (mouseX < scrollAreaWidth + margins) {
794 var progress = Math.min(1, (scrollAreaWidth + margins - mouseX) / (scrollAreaWidth - margins));
795 var contentX = (1 - progress) * (floatingFlickable.contentWidth - floatingFlickable.width)
796 floatingFlickable.contentX = Math.max(0, Math.min(floatingFlickable.contentX, contentX))
798 if (mouseX > floatingFlickable.width - scrollAreaWidth && progressiveScrollingEnabled) {
799 var progress = Math.min(1, (mouseX - (floatingFlickable.width - scrollAreaWidth)) / (scrollAreaWidth - margins))
800 var contentX = progress * (floatingFlickable.contentWidth - floatingFlickable.width)
801 floatingFlickable.contentX = Math.min(floatingFlickable.contentWidth - floatingFlickable.width, Math.max(floatingFlickable.contentX, contentX))
807 mouse.accepted = false;
808 wasTouchPress = mouse.source === Qt.MouseEventSynthesizedByQt;
811 onExited: wasTouchPress = false;
816 id: noAppsRunningHint
818 anchors.horizontalCenter: parent.horizontalCenter
819 anchors.verticalCenter: parent.verticalCenter
821 horizontalAlignment: Qt.AlignHCenter
822 verticalAlignment: Qt.AlignVCenter
823 anchors.leftMargin: root.launcherLeftMargin
824 wrapMode: Label.WordWrap
826 text: i18n.tr("No running apps")
830 target: root.topLevelSurfaceList
831 onListChanged: priv.updateMainAndSideStageIndexes()
836 objectName: "MainStageDropArea"
840 bottom: parent.bottom
842 width: appContainer.width - sideStage.width
843 enabled: priv.sideStageEnabled
846 drop.source.appDelegate.saveStage(ApplicationInfoInterface.MainStage);
847 drop.source.appDelegate.focus = true;
854 objectName: "sideStage"
856 height: appContainer.height
857 x: appContainer.width - width
859 Behavior on opacity { LomiriNumberAnimation {} }
861 if (!priv.mainStageItemId) return 0;
863 if (priv.sideStageItemId && priv.nextInStack > 0) {
865 // Due the order in which bindings are evaluated, this might be triggered while shuffling
866 // the list and index doesn't yet match with itemIndex (even though itemIndex: index)
867 // Let's walk the list and compare itemIndex to make sure we have the correct one.
868 var nextDelegateInStack = -1;
869 for (var i = 0; i < appRepeater.count; i++) {
870 if (appRepeater.itemAt(i).itemIndex == priv.nextInStack) {
871 nextDelegateInStack = appRepeater.itemAt(i);
876 if (nextDelegateInStack.stage === ApplicationInfoInterface.MainStage) {
877 // if the next app in stack is a main stage app, put the sidestage on top of it.
887 if (!shown && priv.mainStageDelegate && !root.spreadShown) {
888 priv.mainStageDelegate.activate();
893 id: sideStageDropArea
894 objectName: "SideStageDropArea"
897 property bool dropAllowed: true
900 dropAllowed = drag.keys != "Disabled";
906 if (drop.keys == "MainStage") {
907 drop.source.appDelegate.saveStage(ApplicationInfoInterface.SideStage);
908 drop.source.appDelegate.focus = true;
913 if (!sideStageDropArea.drag.source) {
923 property real previewScale: .5
924 height: (screensAndWorkspaces.height - units.gu(8)) / 2
926 width: implicitWidth * height / implicitHeight
929 opacity: surface != null ? 1 : 0
930 Behavior on opacity { LomiriNumberAnimation {} }
932 enabled: workspaceSwitcher
934 Drag.active: surface != null
935 Drag.keys: ["application"]
942 model: topLevelSurfaceList
943 objectName: "appRepeater"
945 function indexOf(delegateItem) {
946 for (var i = 0; i < count; i++) {
947 if (itemAt(i) === delegateItem) {
954 delegate: FocusScope {
956 objectName: "appDelegate_" + model.window.id
957 property int itemIndex: index // We need this from outside the repeater
958 // z might be overriden in some cases by effects, but we need z ordering
959 // to calculate occlusion detection
960 property int normalZ: topLevelSurfaceList.count - index
962 if (visuallyMaximized) {
963 priv.updateForegroundMaximizedApp();
968 opacity: fakeDragItem.surface == model.window.surface && fakeDragItem.Drag.active ? 0 : 1
969 Behavior on opacity { LomiriNumberAnimation {} }
971 // Set these as propertyes as they wont update otherwise
972 property real screenOffsetX: Screen.virtualX
973 property real screenOffsetY: Screen.virtualY
975 // Normally we want x/y where the surface thinks it is. Width/height of our delegate will
976 // match what the actual surface size is.
977 // Don't write to those, they will be set by states
979 // Here we will also need to remove the screen offset from miral's results
980 // as lomiri x,y will be relative to the current screen only
981 // FIXME: when proper multiscreen lands
982 x: model.window.position.x - clientAreaItem.x - screenOffsetX
983 y: model.window.position.y - clientAreaItem.y - screenOffsetY
984 width: decoratedWindow.implicitWidth
985 height: decoratedWindow.implicitHeight
987 // requestedX/Y/width/height is what we ask the actual surface to be.
988 // Do not write to those, they will be set by states
989 property real requestedX: windowedX
990 property real requestedY: windowedY
991 property real requestedWidth: windowedWidth
992 property real requestedHeight: windowedHeight
994 // For both windowed and staged need to tell miral what screen we are on,
995 // so we need to add the screen offset to the position we tell miral
996 // FIXME: when proper multiscreen lands
998 target: model.window; property: "requestedPosition"
999 // miral doesn't know about our window decorations. So we have to deduct them
1000 value: Qt.point(appDelegate.requestedX + appDelegate.clientAreaItem.x + screenOffsetX,
1001 appDelegate.requestedY + appDelegate.clientAreaItem.y + screenOffsetY)
1002 when: root.mode == "windowed"
1005 target: model.window; property: "requestedPosition"
1006 value: Qt.point(screenOffsetX, screenOffsetY)
1007 when: root.mode != "windowed"
1010 // In those are for windowed mode. Those values basically store the window's properties
1011 // when having a floating window. If you want to move/resize a window in normal mode, this is what you want to write to.
1012 property real windowedX
1013 property real windowedY
1014 property real windowedWidth
1015 property real windowedHeight
1017 // unlike windowedX/Y, this is the last known grab position before being pushed against edges/corners
1018 // when restoring, the window should return to these, not to the place where it was dropped near the edge
1019 property real restoredX
1020 property real restoredY
1022 // Keeps track of the window geometry while in normal or restored state
1023 // Useful when returning from some maxmized state or when saving the geometry while maximized
1024 // FIXME: find a better solution
1025 property real normalX: 0
1026 property real normalY: 0
1027 property real normalWidth: 0
1028 property real normalHeight: 0
1029 function updateNormalGeometry() {
1030 if (appDelegate.state == "normal" || appDelegate.state == "restored") {
1031 normalX = appDelegate.requestedX;
1032 normalY = appDelegate.requestedY;
1033 normalWidth = appDelegate.width;
1034 normalHeight = appDelegate.height;
1037 function updateRestoredGeometry() {
1038 if (appDelegate.state == "normal" || appDelegate.state == "restored") {
1039 // save the x/y to restore to
1040 restoredX = appDelegate.x;
1041 restoredY = appDelegate.y;
1047 onXChanged: appDelegate.updateNormalGeometry();
1048 onYChanged: appDelegate.updateNormalGeometry();
1049 onWidthChanged: appDelegate.updateNormalGeometry();
1050 onHeightChanged: appDelegate.updateNormalGeometry();
1053 // True when the Stage is focusing this app and playing its own animation.
1054 // Stays true until the app is unfocused.
1055 // If it is, we don't want to play the slide in/out transition from StageMaths.
1056 // Setting it imperatively is not great, but any declarative solution hits
1057 // race conditions, causing two animations to play for one focus event.
1058 property bool inhibitSlideAnimation: false
1063 value: appDelegate.requestedY -
1064 Math.min(appDelegate.requestedY - root.availableDesktopArea.y,
1065 Math.max(0, priv.virtualKeyboardHeight - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
1066 when: root.oskEnabled && appDelegate.focus && (appDelegate.state == "normal" || appDelegate.state == "restored")
1067 && root.inputMethodRect.height > 0
1070 Behavior on x { id: xBehavior; enabled: priv.closingIndex >= 0; LomiriNumberAnimation { onRunningChanged: if (!running) priv.closingIndex = -1} }
1074 onShellOrientationAngleChanged: {
1075 // at this point decoratedWindow.surfaceOrientationAngle is the old shellOrientationAngle
1076 if (application && application.rotatesWindowContents) {
1077 if (root.state == "windowed") {
1078 var angleDiff = decoratedWindow.surfaceOrientationAngle - shellOrientationAngle;
1079 angleDiff = (360 + angleDiff) % 360;
1080 if (angleDiff === 90 || angleDiff === 270) {
1081 var aux = decoratedWindow.requestedHeight;
1082 decoratedWindow.requestedHeight = decoratedWindow.requestedWidth + decoratedWindow.actualDecorationHeight;
1083 decoratedWindow.requestedWidth = aux - decoratedWindow.actualDecorationHeight;
1086 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
1088 decoratedWindow.surfaceOrientationAngle = 0;
1093 readonly property alias application: decoratedWindow.application
1094 readonly property alias minimumWidth: decoratedWindow.minimumWidth
1095 readonly property alias minimumHeight: decoratedWindow.minimumHeight
1096 readonly property alias maximumWidth: decoratedWindow.maximumWidth
1097 readonly property alias maximumHeight: decoratedWindow.maximumHeight
1098 readonly property alias widthIncrement: decoratedWindow.widthIncrement
1099 readonly property alias heightIncrement: decoratedWindow.heightIncrement
1101 readonly property bool maximized: windowState === WindowStateStorage.WindowStateMaximized
1102 readonly property bool maximizedLeft: windowState === WindowStateStorage.WindowStateMaximizedLeft
1103 readonly property bool maximizedRight: windowState === WindowStateStorage.WindowStateMaximizedRight
1104 readonly property bool maximizedHorizontally: windowState === WindowStateStorage.WindowStateMaximizedHorizontally
1105 readonly property bool maximizedVertically: windowState === WindowStateStorage.WindowStateMaximizedVertically
1106 readonly property bool maximizedTopLeft: windowState === WindowStateStorage.WindowStateMaximizedTopLeft
1107 readonly property bool maximizedTopRight: windowState === WindowStateStorage.WindowStateMaximizedTopRight
1108 readonly property bool maximizedBottomLeft: windowState === WindowStateStorage.WindowStateMaximizedBottomLeft
1109 readonly property bool maximizedBottomRight: windowState === WindowStateStorage.WindowStateMaximizedBottomRight
1110 readonly property bool anyMaximized: maximized || maximizedLeft || maximizedRight || maximizedHorizontally || maximizedVertically ||
1111 maximizedTopLeft || maximizedTopRight || maximizedBottomLeft || maximizedBottomRight
1113 readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
1114 readonly property bool fullscreen: windowState === WindowStateStorage.WindowStateFullscreen
1116 readonly property bool canBeMaximized: canBeMaximizedHorizontally && canBeMaximizedVertically
1117 readonly property bool canBeMaximizedLeftRight: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
1118 (maximumHeight == 0 || maximumHeight >= appContainer.height)
1119 readonly property bool canBeCornerMaximized: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
1120 (maximumHeight == 0 || maximumHeight >= appContainer.height/2)
1121 readonly property bool canBeMaximizedHorizontally: maximumWidth == 0 || maximumWidth >= appContainer.width
1122 readonly property bool canBeMaximizedVertically: maximumHeight == 0 || maximumHeight >= appContainer.height
1123 readonly property alias orientationChangesEnabled: decoratedWindow.orientationChangesEnabled
1125 // TODO drop our own windowType once Mir/Miral/Qtmir gets in sync with ours
1126 property int windowState: WindowStateStorage.WindowStateNormal
1127 property int prevWindowState: WindowStateStorage.WindowStateRestored
1129 property bool animationsEnabled: true
1130 property alias title: decoratedWindow.title
1131 readonly property string appName: model.application ? model.application.name : ""
1132 property bool visuallyMaximized: false
1133 property bool visuallyMinimized: false
1134 readonly property alias windowedTransitionRunning: windowedTransition.running
1136 property int stage: ApplicationInfoInterface.MainStage
1137 function saveStage(newStage) {
1138 appDelegate.stage = newStage;
1139 WindowStateStorage.saveStage(appId, newStage);
1140 priv.updateMainAndSideStageIndexes()
1143 readonly property var surface: model.window.surface
1144 readonly property var window: model.window
1146 readonly property alias focusedSurface: decoratedWindow.focusedSurface
1147 readonly property bool dragging: touchControls.overlayShown ? touchControls.dragging : decoratedWindow.dragging
1149 readonly property string appId: model.application.appId
1150 readonly property alias clientAreaItem: decoratedWindow.clientAreaItem
1152 function activate() {
1153 if (model.window.focused) {
1154 updateQmlFocusFromMirSurfaceFocus();
1156 model.window.activate();
1159 function requestMaximize() { model.window.requestState(Mir.MaximizedState); }
1160 function requestMaximizeVertically() { model.window.requestState(Mir.VertMaximizedState); }
1161 function requestMaximizeHorizontally() { model.window.requestState(Mir.HorizMaximizedState); }
1162 function requestMaximizeLeft() { model.window.requestState(Mir.MaximizedLeftState); }
1163 function requestMaximizeRight() { model.window.requestState(Mir.MaximizedRightState); }
1164 function requestMaximizeTopLeft() { model.window.requestState(Mir.MaximizedTopLeftState); }
1165 function requestMaximizeTopRight() { model.window.requestState(Mir.MaximizedTopRightState); }
1166 function requestMaximizeBottomLeft() { model.window.requestState(Mir.MaximizedBottomLeftState); }
1167 function requestMaximizeBottomRight() { model.window.requestState(Mir.MaximizedBottomRightState); }
1168 function requestMinimize() { model.window.requestState(Mir.MinimizedState); }
1169 function requestRestore() { model.window.requestState(Mir.RestoredState); }
1171 function claimFocus() {
1172 if (root.state == "spread") {
1173 spreadItem.highlightedIndex = index
1175 if (root.mode == "stagedWithSideStage") {
1176 if (appDelegate.stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
1179 priv.updateMainAndSideStageIndexes();
1181 appDelegate.focus = true;
1183 // Don't set focusedAppDelegate (and signal mainAppChanged) unnecessarily
1184 // which can happen after getting interactive again.
1185 if (priv.focusedAppDelegate !== appDelegate)
1186 priv.focusedAppDelegate = appDelegate;
1189 function updateQmlFocusFromMirSurfaceFocus() {
1190 if (model.window.focused) {
1192 decoratedWindow.focus = true;
1197 id: windowStateSaver
1199 screenWidth: appContainer.width
1200 screenHeight: appContainer.height
1201 leftMargin: root.availableDesktopArea.x
1202 minimumY: root.availableDesktopArea.y
1206 target: model.window
1208 updateQmlFocusFromMirSurfaceFocus();
1209 if (!model.window.focused) {
1210 inhibitSlideAnimation = false;
1214 appDelegate.activate();
1217 if (value == Mir.MinimizedState) {
1218 appDelegate.minimize();
1219 } else if (value == Mir.MaximizedState) {
1220 appDelegate.maximize();
1221 } else if (value == Mir.VertMaximizedState) {
1222 appDelegate.maximizeVertically();
1223 } else if (value == Mir.HorizMaximizedState) {
1224 appDelegate.maximizeHorizontally();
1225 } else if (value == Mir.MaximizedLeftState) {
1226 appDelegate.maximizeLeft();
1227 } else if (value == Mir.MaximizedRightState) {
1228 appDelegate.maximizeRight();
1229 } else if (value == Mir.MaximizedTopLeftState) {
1230 appDelegate.maximizeTopLeft();
1231 } else if (value == Mir.MaximizedTopRightState) {
1232 appDelegate.maximizeTopRight();
1233 } else if (value == Mir.MaximizedBottomLeftState) {
1234 appDelegate.maximizeBottomLeft();
1235 } else if (value == Mir.MaximizedBottomRightState) {
1236 appDelegate.maximizeBottomRight();
1237 } else if (value == Mir.RestoredState) {
1238 if (appDelegate.fullscreen && appDelegate.prevWindowState != WindowStateStorage.WindowStateRestored
1239 && appDelegate.prevWindowState != WindowStateStorage.WindowStateNormal) {
1240 model.window.requestState(WindowStateStorage.toMirState(appDelegate.prevWindowState));
1242 appDelegate.restore();
1244 } else if (value == Mir.FullscreenState) {
1245 appDelegate.prevWindowState = appDelegate.windowState;
1246 appDelegate.windowState = WindowStateStorage.WindowStateFullscreen;
1251 readonly property bool windowReady: clientAreaItem.surfaceInitialized
1252 onWindowReadyChanged: {
1254 var loadedMirState = WindowStateStorage.toMirState(windowStateSaver.loadedState);
1255 var state = loadedMirState;
1257 if (window.state == Mir.FullscreenState) {
1258 // If the app is fullscreen at startup, we should not use saved state
1259 // Example of why: if you open game that only requests fullscreen at
1260 // Statup, this will automaticly be set to "restored state" since
1261 // thats the default value of stateStorage, this will result in the app
1262 // having the "restored state" as it will not make a fullscreen
1263 // call after the app has started.
1264 console.log("Inital window state is fullscreen, not using saved state.");
1265 state = window.state;
1266 } else if (loadedMirState == Mir.FullscreenState) {
1267 // If saved state is fullscreen, we should use app inital state
1268 // Example of why: if you open browser with youtube video at fullscreen
1269 // and close this app, it will be fullscreen next time you open the app.
1270 console.log("Saved window state is fullscreen, using inital window state");
1271 state = window.state;
1274 // need to apply the shell chrome policy on top the saved window state
1276 if (root.mode == "windowed") {
1277 policy = windowedFullscreenPolicy;
1279 policy = stagedFullscreenPolicy
1281 window.requestState(policy.applyPolicy(state, surface.shellChrome));
1285 Component.onCompleted: {
1286 if (application && application.rotatesWindowContents) {
1287 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
1289 decoratedWindow.surfaceOrientationAngle = 0;
1292 // First, cascade the newly created window, relative to the currently/old focused window.
1293 windowedX = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedX + units.gu(3) : (normalZ - 1) * units.gu(3)
1294 windowedY = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedY + units.gu(3) : normalZ * units.gu(3)
1295 // Now load any saved state. This needs to happen *after* the cascading!
1296 windowStateSaver.load();
1298 updateQmlFocusFromMirSurfaceFocus();
1301 _constructing = false;
1303 Component.onDestruction: {
1304 windowStateSaver.save();
1307 // This stage is about to be destroyed. Don't mess up with the model at this point
1311 if (visuallyMaximized) {
1312 priv.updateForegroundMaximizedApp();
1316 onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
1318 property bool _constructing: true;
1320 if (!_constructing) {
1321 priv.updateMainAndSideStageIndexes();
1327 && !greeter.fullyShown
1328 && (priv.foregroundMaximizedAppDelegate === null || priv.foregroundMaximizedAppDelegate.normalZ <= z)
1330 || appDelegate.fullscreen
1331 || focusAnimation.running || rightEdgeFocusAnimation.running || hidingAnimation.running
1334 model.window.close();
1337 function maximize(animated) {
1338 animationsEnabled = (animated === undefined) || animated;
1339 windowState = WindowStateStorage.WindowStateMaximized;
1341 function maximizeLeft(animated) {
1342 animationsEnabled = (animated === undefined) || animated;
1343 windowState = WindowStateStorage.WindowStateMaximizedLeft;
1345 function maximizeRight(animated) {
1346 animationsEnabled = (animated === undefined) || animated;
1347 windowState = WindowStateStorage.WindowStateMaximizedRight;
1349 function maximizeHorizontally(animated) {
1350 animationsEnabled = (animated === undefined) || animated;
1351 windowState = WindowStateStorage.WindowStateMaximizedHorizontally;
1353 function maximizeVertically(animated) {
1354 animationsEnabled = (animated === undefined) || animated;
1355 windowState = WindowStateStorage.WindowStateMaximizedVertically;
1357 function maximizeTopLeft(animated) {
1358 animationsEnabled = (animated === undefined) || animated;
1359 windowState = WindowStateStorage.WindowStateMaximizedTopLeft;
1361 function maximizeTopRight(animated) {
1362 animationsEnabled = (animated === undefined) || animated;
1363 windowState = WindowStateStorage.WindowStateMaximizedTopRight;
1365 function maximizeBottomLeft(animated) {
1366 animationsEnabled = (animated === undefined) || animated;
1367 windowState = WindowStateStorage.WindowStateMaximizedBottomLeft;
1369 function maximizeBottomRight(animated) {
1370 animationsEnabled = (animated === undefined) || animated;
1371 windowState = WindowStateStorage.WindowStateMaximizedBottomRight;
1373 function minimize(animated) {
1374 animationsEnabled = (animated === undefined) || animated;
1375 windowState |= WindowStateStorage.WindowStateMinimized; // add the minimized bit
1377 function restore(animated,state) {
1378 animationsEnabled = (animated === undefined) || animated;
1379 windowState = state || WindowStateStorage.WindowStateRestored;
1380 windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
1381 prevWindowState = windowState;
1384 function playFocusAnimation() {
1385 if (state == "stagedRightEdge") {
1386 // TODO: Can we drop this if and find something that always works?
1387 if (root.mode == "staged") {
1388 rightEdgeFocusAnimation.targetX = 0
1389 rightEdgeFocusAnimation.start()
1390 } else if (root.mode == "stagedWithSideStage") {
1391 rightEdgeFocusAnimation.targetX = appDelegate.stage == ApplicationInfoInterface.SideStage ? sideStage.x : 0
1392 rightEdgeFocusAnimation.start()
1394 } else if (state == "windowedRightEdge" || state == "windowed") {
1397 focusAnimation.start()
1400 function playHidingAnimation() {
1401 if (state != "windowedRightEdge") {
1402 hidingAnimation.start()
1406 function refreshStage() {
1407 var newStage = ApplicationInfoInterface.MainStage;
1408 if (priv.sideStageEnabled) { // we're in lanscape rotation.
1409 if (application && application.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {
1410 var defaultStage = ApplicationInfoInterface.SideStage; // if application supports portrait, it defaults to sidestage.
1411 if (application.supportedOrientations & (Qt.LandscapeOrientation|Qt.InvertedLandscapeOrientation)) {
1412 // if it supports lanscape, it defaults to mainstage.
1413 defaultStage = ApplicationInfoInterface.MainStage;
1415 newStage = WindowStateStorage.getStage(application.appId, defaultStage);
1420 if (focus && stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
1425 LomiriNumberAnimation {
1431 duration: LomiriAnimation.SnapDuration
1433 topLevelSurfaceList.pendingActivation();
1434 topLevelSurfaceList.raiseId(model.window.id);
1437 appDelegate.activate();
1441 id: rightEdgeFocusAnimation
1442 property int targetX: 0
1443 LomiriNumberAnimation { target: appDelegate; properties: "x"; to: rightEdgeFocusAnimation.targetX; duration: priv.animationDuration }
1444 LomiriNumberAnimation { target: decoratedWindow; properties: "angle"; to: 0; duration: priv.animationDuration }
1445 LomiriNumberAnimation { target: decoratedWindow; properties: "itemScale"; to: 1; duration: priv.animationDuration }
1447 topLevelSurfaceList.pendingActivation();
1448 inhibitSlideAnimation = true;
1451 appDelegate.activate();
1456 LomiriNumberAnimation { target: appDelegate; property: "opacity"; to: 0; duration: priv.animationDuration }
1457 onStopped: appDelegate.opacity = 1
1464 flickable: floatingFlickable
1468 sceneWidth: root.width
1469 stage: appDelegate.stage
1470 thisDelegate: appDelegate
1471 mainStageDelegate: priv.mainStageDelegate
1472 sideStageDelegate: priv.sideStageDelegate
1473 sideStageWidth: sideStage.panelWidth
1474 sideStageHandleWidth: sideStage.handleWidth
1475 sideStageX: sideStage.x
1476 itemIndex: appDelegate.itemIndex
1477 nextInStack: priv.nextInStack
1478 animationDuration: priv.animationDuration
1481 StagedRightEdgeMaths {
1482 id: stagedRightEdgeMaths
1483 sceneWidth: root.availableDesktopArea.width
1484 sceneHeight: appContainer.height
1485 isMainStageApp: priv.mainStageDelegate == appDelegate
1486 isSideStageApp: priv.sideStageDelegate == appDelegate
1487 sideStageWidth: sideStage.width
1488 sideStageOpen: sideStage.shown
1490 nextInStack: priv.nextInStack
1492 targetHeight: spreadItem.stackHeight
1493 targetX: spreadMaths.targetX
1494 startY: appDelegate.fullscreen ? 0 : root.availableDesktopArea.y
1495 targetY: spreadMaths.targetY
1496 targetAngle: spreadMaths.targetAngle
1497 targetScale: spreadMaths.targetScale
1498 shuffledZ: stageMaths.itemZ
1499 breakPoint: spreadItem.rightEdgeBreakPoint
1502 WindowedRightEdgeMaths {
1503 id: windowedRightEdgeMaths
1505 startWidth: appDelegate.requestedWidth
1506 startHeight: appDelegate.requestedHeight
1507 targetHeight: spreadItem.stackHeight
1508 targetX: spreadMaths.targetX
1509 targetY: spreadMaths.targetY
1510 normalZ: appDelegate.normalZ
1511 targetAngle: spreadMaths.targetAngle
1512 targetScale: spreadMaths.targetScale
1513 breakPoint: spreadItem.rightEdgeBreakPoint
1518 name: "spread"; when: root.state == "spread"
1519 StateChangeScript { script: { decoratedWindow.cancelDrag(); } }
1521 target: decoratedWindow;
1522 showDecoration: false;
1523 angle: spreadMaths.targetAngle
1524 itemScale: spreadMaths.targetScale
1525 scaleToPreviewSize: spreadItem.stackHeight
1526 scaleToPreviewProgress: 1
1527 hasDecoration: root.mode === "windowed"
1528 shadowOpacity: spreadMaths.shadowOpacity
1529 showHighlight: spreadItem.highlightedIndex === index
1530 darkening: spreadItem.highlightedIndex >= 0
1531 anchors.topMargin: dragArea.distance
1535 x: spreadMaths.targetX
1536 y: spreadMaths.targetY
1538 height: spreadItem.spreadItemHeight
1539 visible: spreadMaths.itemVisible
1541 PropertyChanges { target: dragArea; enabled: true }
1542 PropertyChanges { target: windowInfoItem; opacity: spreadMaths.tileInfoOpacity; visible: spreadMaths.itemVisible }
1543 PropertyChanges { target: touchControls; enabled: false }
1546 name: "stagedRightEdge"
1547 when: (root.mode == "staged" || root.mode == "stagedWithSideStage") && (root.state == "sideStagedRightEdge" || root.state == "stagedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running)
1549 target: stagedRightEdgeMaths
1550 progress: Math.max(rightEdgePushProgress, rightEdgeDragArea.draggedProgress)
1554 x: stagedRightEdgeMaths.animatedX
1555 y: stagedRightEdgeMaths.animatedY
1556 z: stagedRightEdgeMaths.animatedZ
1557 height: stagedRightEdgeMaths.animatedHeight
1558 visible: appDelegate.x < root.width
1561 target: decoratedWindow
1562 hasDecoration: false
1563 angle: stagedRightEdgeMaths.animatedAngle
1564 itemScale: stagedRightEdgeMaths.animatedScale
1565 scaleToPreviewSize: spreadItem.stackHeight
1566 scaleToPreviewProgress: stagedRightEdgeMaths.scaleToPreviewProgress
1569 // make sure it's visible but transparent so it fades in when we transition to spread
1570 PropertyChanges { target: windowInfoItem; opacity: 0; visible: true }
1573 name: "windowedRightEdge"
1574 when: root.mode == "windowed" && (root.state == "windowedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running || rightEdgePushProgress > 0)
1576 target: windowedRightEdgeMaths
1577 swipeProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0
1578 pushProgress: rightEdgePushProgress
1582 x: windowedRightEdgeMaths.animatedX
1583 y: windowedRightEdgeMaths.animatedY
1584 z: windowedRightEdgeMaths.animatedZ
1585 height: stagedRightEdgeMaths.animatedHeight
1588 target: decoratedWindow
1589 showDecoration: windowedRightEdgeMaths.decorationHeight
1590 angle: windowedRightEdgeMaths.animatedAngle
1591 itemScale: windowedRightEdgeMaths.animatedScale
1592 scaleToPreviewSize: spreadItem.stackHeight
1593 scaleToPreviewProgress: windowedRightEdgeMaths.scaleToPreviewProgress
1597 target: opacityEffect;
1598 opacityValue: windowedRightEdgeMaths.opacityMask
1599 sourceItem: windowedRightEdgeMaths.opacityMask < 1 ? decoratedWindow : null
1603 name: "staged"; when: root.state == "staged"
1607 y: root.availableDesktopArea.y
1608 visuallyMaximized: true
1609 visible: appDelegate.x < root.width
1613 requestedWidth: appContainer.width
1614 requestedHeight: root.availableDesktopArea.height
1615 restoreEntryValues: false
1618 target: decoratedWindow
1619 hasDecoration: false
1627 animateX: !focusAnimation.running && !rightEdgeFocusAnimation.running && itemIndex !== spreadItem.highlightedIndex && !inhibitSlideAnimation
1630 target: appDelegate.window
1631 allowClientResize: false
1635 name: "stagedWithSideStage"; when: root.state == "stagedWithSideStage"
1643 y: root.availableDesktopArea.y
1645 visuallyMaximized: true
1646 visible: appDelegate.x < root.width
1650 requestedWidth: stageMaths.itemWidth
1651 requestedHeight: root.availableDesktopArea.height
1652 restoreEntryValues: false
1655 target: decoratedWindow
1656 hasDecoration: false
1663 target: appDelegate.window
1664 allowClientResize: false
1668 name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
1670 target: appDelegate;
1671 requestedX: root.availableDesktopArea.x;
1673 visuallyMinimized: false;
1674 visuallyMaximized: true
1678 requestedWidth: root.availableDesktopArea.width;
1679 requestedHeight: appContainer.height;
1680 restoreEntryValues: false
1682 PropertyChanges { target: touchControls; enabled: true }
1683 PropertyChanges { target: decoratedWindow; windowControlButtonsVisible: false }
1686 name: "fullscreen"; when: appDelegate.fullscreen && !appDelegate.minimized
1688 target: appDelegate;
1694 requestedWidth: appContainer.width
1695 requestedHeight: appContainer.height
1696 restoreEntryValues: false
1698 PropertyChanges { target: decoratedWindow; hasDecoration: false }
1702 when: appDelegate.windowState == WindowStateStorage.WindowStateNormal
1705 visuallyMinimized: false
1707 PropertyChanges { target: touchControls; enabled: true }
1708 PropertyChanges { target: resizeArea; enabled: true }
1709 PropertyChanges { target: decoratedWindow; shadowOpacity: .3; windowControlButtonsVisible: true}
1712 requestedWidth: windowedWidth
1713 requestedHeight: windowedHeight
1714 restoreEntryValues: false
1719 when: appDelegate.windowState == WindowStateStorage.WindowStateRestored
1722 restoreEntryValues: false
1723 target: appDelegate;
1724 windowedX: restoredX;
1725 windowedY: restoredY;
1729 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
1733 windowedX: root.availableDesktopArea.x
1734 windowedY: root.availableDesktopArea.y
1735 windowedWidth: root.availableDesktopArea.width / 2
1736 windowedHeight: root.availableDesktopArea.height
1740 name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
1741 extend: "maximizedLeft"
1743 target: appDelegate;
1744 windowedX: root.availableDesktopArea.x + (root.availableDesktopArea.width / 2)
1748 name: "maximizedTopLeft"; when: appDelegate.maximizedTopLeft && !appDelegate.minimized
1752 windowedX: root.availableDesktopArea.x
1753 windowedY: root.availableDesktopArea.y
1754 windowedWidth: root.availableDesktopArea.width / 2
1755 windowedHeight: root.availableDesktopArea.height / 2
1759 name: "maximizedTopRight"; when: appDelegate.maximizedTopRight && !appDelegate.minimized
1760 extend: "maximizedTopLeft"
1763 windowedX: root.availableDesktopArea.x + (root.availableDesktopArea.width / 2)
1767 name: "maximizedBottomLeft"; when: appDelegate.maximizedBottomLeft && !appDelegate.minimized
1771 windowedX: root.availableDesktopArea.x
1772 windowedY: root.availableDesktopArea.y + (root.availableDesktopArea.height / 2)
1773 windowedWidth: root.availableDesktopArea.width / 2
1774 windowedHeight: root.availableDesktopArea.height / 2
1778 name: "maximizedBottomRight"; when: appDelegate.maximizedBottomRight && !appDelegate.minimized
1779 extend: "maximizedBottomLeft"
1782 windowedX: root.availableDesktopArea.x + (root.availableDesktopArea.width / 2)
1786 name: "maximizedHorizontally"; when: appDelegate.maximizedHorizontally && !appDelegate.minimized
1790 windowedX: root.availableDesktopArea.x; windowedY: windowedY
1791 windowedWidth: root.availableDesktopArea.width; windowedHeight: windowedHeight
1795 name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
1799 windowedX: windowedX; windowedY: root.availableDesktopArea.y
1800 windowedWidth: windowedWidth; windowedHeight: root.availableDesktopArea.height
1804 name: "minimized"; when: appDelegate.minimized
1807 scale: units.gu(5) / appDelegate.width
1809 visuallyMinimized: true
1810 visuallyMaximized: false
1811 x: -appDelegate.width / 2
1819 // These two animate applications into position from Staged to Desktop and back
1821 from: "staged,stagedWithSideStage"
1822 to: "normal,restored,maximized,maximizedHorizontally,maximizedVertically,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedBottomLeft,maximizedTopRight,maximizedBottomRight"
1823 enabled: appDelegate.animationsEnabled
1824 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
1825 LomiriNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,opacity,requestedWidth,requestedHeight,scale"; duration: priv.animationDuration }
1828 from: "normal,restored,maximized,maximizedHorizontally,maximizedVertically,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedBottomLeft,maximizedTopRight,maximizedBottomRight"
1829 to: "staged,stagedWithSideStage"
1830 LomiriNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,requestedWidth,requestedHeight"; duration: priv.animationDuration}
1834 from: "normal,restored,maximized,maximizedHorizontally,maximizedVertically,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedBottomLeft,maximizedTopRight,maximizedBottomRight,staged,stagedWithSideStage,windowedRightEdge,stagedRightEdge";
1836 // DecoratedWindow wants the scaleToPreviewSize set before enabling scaleToPreview
1837 PropertyAction { target: appDelegate; properties: "z,visible" }
1838 PropertyAction { target: decoratedWindow; property: "scaleToPreviewSize" }
1839 LomiriNumberAnimation { target: appDelegate; properties: "x,y,height"; duration: priv.animationDuration }
1840 LomiriNumberAnimation { target: decoratedWindow; properties: "width,height,itemScale,angle,scaleToPreviewProgress"; duration: priv.animationDuration }
1841 LomiriNumberAnimation { target: windowInfoItem; properties: "opacity"; duration: priv.animationDuration }
1844 from: "normal,staged"; to: "stagedWithSideStage"
1845 LomiriNumberAnimation { target: appDelegate; properties: "x,y,requestedWidth,requestedHeight"; duration: priv.animationDuration }
1848 to: "windowedRightEdge"
1851 windowedRightEdgeMaths.startX = appDelegate.requestedX
1852 windowedRightEdgeMaths.startY = appDelegate.requestedY
1855 var thisRect = { x: appDelegate.windowedX, y: appDelegate.windowedY, width: appDelegate.requestedWidth, height: appDelegate.requestedHeight }
1856 var otherDelegate = appRepeater.itemAt(0);
1857 var otherRect = { x: otherDelegate.windowedX, y: otherDelegate.windowedY, width: otherDelegate.requestedWidth, height: otherDelegate.requestedHeight }
1858 var intersectionRect = MathUtils.intersectionRect(thisRect, otherRect)
1859 var mappedInterSectionRect = appDelegate.mapFromItem(root, intersectionRect.x, intersectionRect.y)
1860 opacityEffect.maskX = mappedInterSectionRect.x
1861 opacityEffect.maskY = mappedInterSectionRect.y
1862 opacityEffect.maskWidth = intersectionRect.width
1863 opacityEffect.maskHeight = intersectionRect.height
1869 from: "stagedRightEdge"; to: "staged"
1870 enabled: rightEdgeDragArea.cancelled // only transition back to state if the gesture was cancelled, in the other cases we play the focusAnimations.
1871 SequentialAnimation {
1873 LomiriNumberAnimation { target: appDelegate; properties: "x,y,height,width,scale"; duration: priv.animationDuration }
1874 LomiriNumberAnimation { target: decoratedWindow; properties: "width,height,itemScale,angle,scaleToPreviewProgress"; duration: priv.animationDuration }
1876 // We need to release scaleToPreviewSize at last
1877 PropertyAction { target: decoratedWindow; property: "scaleToPreviewSize" }
1878 PropertyAction { target: appDelegate; property: "visible" }
1882 from: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1884 SequentialAnimation {
1885 ScriptAction { script: { fakeRectangle.stop(); } }
1886 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1887 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1888 LomiriNumberAnimation { target: appDelegate; properties: "x,y,scale,opacity"; duration: priv.animationDuration }
1889 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1894 to: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1895 SequentialAnimation {
1896 PropertyAction { target: appDelegate; property: "visuallyMinimized,z" }
1898 LomiriNumberAnimation { target: appDelegate; properties: "x"; from: -appDelegate.width / 2; duration: priv.animationDuration }
1899 LomiriNumberAnimation { target: appDelegate; properties: "y,opacity"; duration: priv.animationDuration }
1900 LomiriNumberAnimation { target: appDelegate; properties: "scale"; from: 0; duration: priv.animationDuration }
1902 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1906 id: windowedTransition
1907 from: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen,minimized"
1908 to: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1909 enabled: appDelegate.animationsEnabled
1910 SequentialAnimation {
1911 ScriptAction { script: {
1912 if (appDelegate.visuallyMaximized) visuallyMaximized = false; // maximized before -> going to restored
1915 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1916 LomiriNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,windowedX,windowedY,opacity,scale,requestedWidth,requestedHeight,windowedWidth,windowedHeight";
1917 duration: priv.animationDuration }
1918 ScriptAction { script: {
1919 fakeRectangle.stop();
1920 appDelegate.visuallyMaximized = appDelegate.maximized; // reflect the target state
1929 property: "decorationsAlwaysVisible"
1930 value: appDelegate && appDelegate.maximized && touchControls.overlayShown
1935 objectName: "windowResizeArea"
1937 anchors.fill: appDelegate
1939 // workaround so that it chooses the correct resize borders when you drag from a corner ResizeGrip
1940 anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
1943 boundsItem: root.availableDesktopArea
1944 minWidth: units.gu(10)
1945 minHeight: units.gu(10)
1946 borderThickness: units.gu(2)
1949 readyToAssesBounds: !appDelegate._constructing
1952 appDelegate.activate();
1958 objectName: "decoratedWindow"
1959 anchors.left: appDelegate.left
1960 anchors.top: appDelegate.top
1961 application: model.application
1962 surface: model.window.surface
1963 active: model.window.focused
1965 interactive: root.interactive
1967 decorationHeight: priv.windowDecorationHeight
1968 maximizeButtonShown: appDelegate.canBeMaximized
1969 overlayShown: touchControls.overlayShown
1970 width: implicitWidth
1971 height: implicitHeight
1972 highlightSize: windowInfoItem.iconMargin / 2
1973 boundsItem: root.availableDesktopArea
1974 panelState: root.panelState
1975 altDragEnabled: root.mode == "windowed"
1977 requestedWidth: appDelegate.requestedWidth
1978 requestedHeight: appDelegate.requestedHeight
1980 onCloseClicked: { appDelegate.close(); }
1981 onMaximizeClicked: {
1982 if (appDelegate.canBeMaximized) {
1983 appDelegate.anyMaximized ? appDelegate.requestRestore() : appDelegate.requestMaximize();
1986 onMaximizeHorizontallyClicked: {
1987 if (appDelegate.canBeMaximizedHorizontally) {
1988 appDelegate.maximizedHorizontally ? appDelegate.requestRestore() : appDelegate.requestMaximizeHorizontally()
1991 onMaximizeVerticallyClicked: {
1992 if (appDelegate.canBeMaximizedVertically) {
1993 appDelegate.maximizedVertically ? appDelegate.requestRestore() : appDelegate.requestMaximizeVertically()
1996 onMinimizeClicked: { appDelegate.requestMinimize(); }
1997 onDecorationPressed: { appDelegate.activate(); }
1998 onDecorationReleased: fakeRectangle.visible ? fakeRectangle.commit() : appDelegate.updateRestoredGeometry()
2000 property real angle: 0
2001 Behavior on angle { enabled: priv.closingIndex >= 0; LomiriNumberAnimation {} }
2002 property real itemScale: 1
2003 Behavior on itemScale { enabled: priv.closingIndex >= 0; LomiriNumberAnimation {} }
2008 origin.y: decoratedWindow.implicitHeight / 2
2009 xScale: decoratedWindow.itemScale
2010 yScale: decoratedWindow.itemScale
2013 origin { x: 0; y: (decoratedWindow.height / 2) }
2014 axis { x: 0; y: 1; z: 0 }
2015 angle: decoratedWindow.angle
2022 anchors.fill: decoratedWindow
2025 WindowControlsOverlay {
2027 anchors.fill: appDelegate
2029 resizeArea: resizeArea
2032 boundsItem: root.availableDesktopArea
2034 onFakeMaximizeAnimationRequested: if (!appDelegate.maximized) fakeRectangle.maximize(amount, true)
2035 onFakeMaximizeLeftAnimationRequested: if (!appDelegate.maximizedLeft) fakeRectangle.maximizeLeft(amount, true)
2036 onFakeMaximizeRightAnimationRequested: if (!appDelegate.maximizedRight) fakeRectangle.maximizeRight(amount, true)
2037 onFakeMaximizeTopLeftAnimationRequested: if (!appDelegate.maximizedTopLeft) fakeRectangle.maximizeTopLeft(amount, true);
2038 onFakeMaximizeTopRightAnimationRequested: if (!appDelegate.maximizedTopRight) fakeRectangle.maximizeTopRight(amount, true);
2039 onFakeMaximizeBottomLeftAnimationRequested: if (!appDelegate.maximizedBottomLeft) fakeRectangle.maximizeBottomLeft(amount, true);
2040 onFakeMaximizeBottomRightAnimationRequested: if (!appDelegate.maximizedBottomRight) fakeRectangle.maximizeBottomRight(amount, true);
2041 onStopFakeAnimation: fakeRectangle.stop();
2042 onDragReleased: fakeRectangle.visible ? fakeRectangle.commit() : appDelegate.updateRestoredGeometry()
2045 WindowedFullscreenPolicy {
2046 id: windowedFullscreenPolicy
2048 StagedFullscreenPolicy {
2049 id: stagedFullscreenPolicy
2050 active: root.mode == "staged" || root.mode == "stagedWithSideStage"
2051 surface: model.window.surface
2054 SpreadDelegateInputArea {
2056 objectName: "dragArea"
2057 anchors.fill: decoratedWindow
2061 dragDelegate: fakeDragItem
2064 spreadItem.highlightedIndex = index;
2065 if (distance == 0) {
2066 priv.goneToSpread = false;
2070 priv.closingIndex = index
2071 appDelegate.close();
2077 objectName: "windowInfoItem"
2078 anchors { left: parent.left; top: decoratedWindow.bottom; topMargin: units.gu(1) }
2079 title: model.application.name
2080 iconSource: model.application.icon
2081 height: spreadItem.appInfoHeight
2084 visible: opacity > 0
2086 var nextApp = appRepeater.itemAt(index + 1);
2088 return Math.max(iconHeight, nextApp.x - appDelegate.x - units.gu(1))
2090 return appDelegate.width;
2094 spreadItem.highlightedIndex = index;
2095 priv.goneToSpread = false;
2101 objectName: "closeMouseArea"
2102 anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset }
2103 readonly property var mousePos: hoverMouseArea.mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
2104 readonly property bool shown: dragArea.distance == 0
2105 && index == spreadItem.highlightedIndex
2106 && mousePos.y < (decoratedWindow.height / 3)
2107 && mousePos.y > -units.gu(4)
2108 && mousePos.x > -units.gu(4)
2109 && mousePos.x < (decoratedWindow.width * 2 / 3)
2110 opacity: shown ? 1 : 0
2111 visible: opacity > 0
2112 Behavior on opacity { LomiriNumberAnimation { duration: LomiriAnimation.SnapDuration } }
2117 priv.closingIndex = index;
2118 appDelegate.close();
2122 source: "graphics/window-close.svg"
2123 anchors.fill: closeMouseArea
2124 anchors.margins: units.gu(2)
2125 sourceSize.width: width
2126 sourceSize.height: height
2131 // Group all child windows in this item so that we can fade them out together when going to the spread
2132 // (and fade them in back again when returning from it)
2133 readonly property bool stageOnProperState: root.state === "windowed"
2134 || root.state === "staged"
2135 || root.state === "stagedWithSideStage"
2137 // TODO: Is it worth the extra cost of layering to avoid the opacity artifacts of intersecting children?
2138 // Btw, will involve more than uncommenting the line below as children won't necessarily fit this item's
2139 // geometry. This is just a reference.
2140 //layer.enabled: opacity !== 0.0 && opacity !== 1.0
2142 opacity: stageOnProperState ? 1.0 : 0.0
2143 visible: opacity !== 0.0 // make it transparent to input as well
2144 Behavior on opacity { LomiriNumberAnimation {} }
2147 id: childWindowRepeater
2148 model: appDelegate.surface ? appDelegate.surface.childSurfaceList : null
2150 delegate: ChildWindowTree {
2151 surface: model.surface
2153 // Account for the displacement caused by window decoration in the top-level surface
2154 // Ie, the top-level surface is not positioned at (0,0) of this ChildWindow's parent (appDelegate)
2155 displacementX: appDelegate.clientAreaItem.x
2156 displacementY: appDelegate.clientAreaItem.y
2158 boundsItem: root.availableDesktopArea
2159 decorationHeight: priv.windowDecorationHeight
2161 z: childWindowRepeater.count - model.index
2165 // some child surface in this tree got focus.
2166 // Ensure we also have it at the top-level hierarchy
2167 appDelegate.claimFocus();
2177 FakeMaximizeDelegate {
2179 target: priv.focusedAppDelegate
2180 leftMargin: root.availableDesktopArea.x
2181 appContainerWidth: appContainer.width
2182 appContainerHeight: appContainer.height
2183 panelState: root.panelState
2187 id: workspaceSwitcher
2188 enabled: workspaceEnabled
2189 anchors.centerIn: parent
2190 height: units.gu(20)
2191 width: root.width - units.gu(8)
2192 background: root.background
2195 appContainer.focus = true;
2201 id: shortRightEdgeSwipeAnimation
2204 duration: priv.animationDuration
2208 id: rightEdgeDragArea
2209 objectName: "rightEdgeDragArea"
2210 direction: Direction.Leftwards
2211 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
2212 width: root.dragAreaWidth
2213 enabled: root.spreadEnabled
2215 property var gesturePoints: []
2216 property bool cancelled: false
2218 property real progress: -touchPosition.x / root.width
2219 onProgressChanged: {
2221 draggedProgress = progress;
2225 property real draggedProgress: 0
2227 onTouchPositionChanged: {
2228 gesturePoints.push(touchPosition.x);
2229 if (gesturePoints.length > 10) {
2230 gesturePoints.splice(0, gesturePoints.length - 10)
2234 onDraggingChanged: {
2236 // A potential edge-drag gesture has started. Start recording it
2239 draggedProgress = 0;
2241 // Ok. The user released. Did he drag far enough to go to full spread?
2242 if (gesturePoints[gesturePoints.length - 1] < -spreadItem.rightEdgeBreakPoint * spreadItem.width ) {
2244 // He dragged far enough, but if the last movement was a flick to the right again, he wants to cancel the spread again.
2245 var oneWayFlickToRight = true;
2246 var smallestX = gesturePoints[0]-1;
2247 for (var i = 0; i < gesturePoints.length; i++) {
2248 if (gesturePoints[i] <= smallestX) {
2249 oneWayFlickToRight = false;
2252 smallestX = gesturePoints[i];
2255 if (!oneWayFlickToRight) {
2256 // Ok, the user made it, let's go to spread!
2257 priv.goneToSpread = true;
2262 // Ok, the user didn't drag far enough to cross the breakPoint
2263 // Find out if it was a one-way movement to the left, in which case we just switch directly to next app.
2264 var oneWayFlick = true;
2265 var smallestX = rightEdgeDragArea.width;
2266 for (var i = 0; i < gesturePoints.length; i++) {
2267 if (gesturePoints[i] >= smallestX) {
2268 oneWayFlick = false;
2271 smallestX = gesturePoints[i];
2274 if (appRepeater.count > 1 &&
2275 (oneWayFlick && rightEdgeDragArea.distance > units.gu(2) || rightEdgeDragArea.distance > spreadItem.rightEdgeBreakPoint * spreadItem.width)) {
2276 var nextStage = appRepeater.itemAt(priv.nextInStack).stage
2277 for (var i = 0; i < appRepeater.count; i++) {
2278 if (i != priv.nextInStack && appRepeater.itemAt(i).stage == nextStage) {
2279 appRepeater.itemAt(i).playHidingAnimation()
2283 appRepeater.itemAt(priv.nextInStack).playFocusAnimation()
2284 if (appRepeater.itemAt(priv.nextInStack).stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
2298 TabletSideStageTouchGesture {
2300 objectName: "triGestureArea"
2301 anchors.fill: parent
2303 property Item appDelegate
2305 dragComponent: dragComponent
2306 dragComponentProperties: { "appDelegate": appDelegate }
2309 function matchDelegate(obj) { return String(obj.objectName).indexOf("appDelegate") >= 0; }
2311 var delegateAtCenter = Functions.itemAt(appContainer, x, y, matchDelegate);
2312 if (!delegateAtCenter) return;
2314 appDelegate = delegateAtCenter;
2318 if (sideStage.shown) {
2322 priv.updateMainAndSideStageIndexes()
2327 // If we're dragging to the sidestage.
2328 if (!sideStage.shown) {
2336 property Item appDelegate
2338 surface: appDelegate ? appDelegate.surface : null
2340 consumesInput: false
2343 requestedWidth: appDelegate ? appDelegate.requestedWidth : 0
2344 requestedHeight: appDelegate ? appDelegate.requestedHeight : 0
2347 height: units.gu(40)
2349 Drag.hotSpot.x: width/2
2350 Drag.hotSpot.y: height/2
2351 // only accept opposite stage.
2353 if (!surface) return "Disabled";
2355 if (appDelegate.stage === ApplicationInfo.MainStage) {
2356 if (appDelegate.application.supportedOrientations
2357 & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {