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 Ubuntu.Components 1.3
21 import Unity.Application 0.1
22 import "../Components/PanelState"
23 import "../Components"
25 import Ubuntu.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
54 // Whether outside forces say that the Stage may have focus
55 property bool allowInteractivity
57 readonly property bool interactive: (state === "staged" || state === "stagedWithSideStage" || state === "windowed") && allowInteractivity
60 property string mode: "staged"
62 // Used by the tutorial code
63 readonly property real rightEdgeDragProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0 // How far left the stage has been dragged
65 // used by the snap windows (edge maximize) feature
66 readonly property alias previewRectangle: fakeRectangle
68 readonly property bool spreadShown: state == "spread"
69 readonly property var mainApp: priv.focusedAppDelegate ? priv.focusedAppDelegate.application : null
71 // application windows never rotate independently
72 property int mainAppWindowOrientationAngle: shellOrientationAngle
74 property bool orientationChangesEnabled: !priv.focusedAppDelegate || priv.focusedAppDelegate.orientationChangesEnabled
76 property int supportedOrientations: {
80 return mainApp.supportedOrientations;
81 case "stagedWithSideStage":
82 var orientations = mainApp.supportedOrientations;
83 orientations |= Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation;
84 if (priv.sideStageItemId) {
85 // If we have a sidestage app, support Portrait orientation
86 // so that it will switch the sidestage app to mainstage on rotate to portrait
87 orientations |= Qt.PortraitOrientation|Qt.InvertedPortraitOrientation;
93 return Qt.PortraitOrientation |
94 Qt.LandscapeOrientation |
95 Qt.InvertedPortraitOrientation |
96 Qt.InvertedLandscapeOrientation;
99 property int launcherLeftMargin : 0
102 target: topLevelSurfaceList
103 property: "rootFocus"
107 onInteractiveChanged: {
108 // Stage must have focus before activating windows, including null
114 onAltTabPressedChanged: {
117 if (root.spreadEnabled) {
118 altTabDelayTimer.start();
121 // Alt Tab has been released, did we already go to spread?
122 if (priv.goneToSpread) {
123 priv.goneToSpread = false;
125 // No we didn't, do a quick alt-tab
126 if (appRepeater.count > 1) {
127 appRepeater.itemAt(1).activate();
128 } else if (appRepeater.count > 0) {
129 appRepeater.itemAt(0).activate(); // quick alt-tab to the only (minimized) window should still activate it
140 if (root.altTabPressed) {
141 priv.goneToSpread = true;
146 // For MirAL window management
148 normal: Qt.rect(0, root.mode === "windowed" ? priv.windowDecorationHeight : 0, 0, 0)
152 property Item itemConfiningMouseCursor: !spreadShown && priv.focusedAppDelegate && priv.focusedAppDelegate.window.confinesMousePointer ?
153 priv.focusedAppDelegate.clientAreaItem : null;
155 signal itemSnapshotRequested(Item item)
157 // functions to be called from outside
158 function updateFocusedAppOrientation() { /* TODO */ }
159 function updateFocusedAppOrientationAnimated() { /* TODO */}
161 function closeSpread() {
162 priv.goneToSpread = false;
165 onSpreadEnabledChanged: {
166 if (!spreadEnabled && spreadShown) {
171 onRightEdgePushProgressChanged: {
172 if (spreadEnabled && rightEdgePushProgress >= 1) {
173 priv.goneToSpread = true
178 id: lifecycleExceptions
179 schema.id: "com.canonical.qtmir"
182 function isExemptFromLifecycle(appId) {
183 var shortAppId = appId.split('_')[0];
184 for (var i = 0; i < lifecycleExceptions.lifecycleExemptAppids.length; i++) {
185 if (shortAppId === lifecycleExceptions.lifecycleExemptAppids[i]) {
193 id: closeFocusedShortcut
194 shortcut: Qt.AltModifier|Qt.Key_F4
196 if (priv.focusedAppDelegate) {
197 priv.focusedAppDelegate.close();
203 id: showSpreadShortcut
204 shortcut: Qt.MetaModifier|Qt.Key_W
205 active: root.spreadEnabled
206 onTriggered: priv.goneToSpread = true
210 id: minimizeAllShortcut
211 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_D
212 onTriggered: priv.minimizeAllWindows()
213 active: root.state == "windowed"
217 id: maximizeWindowShortcut
218 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up
219 onTriggered: priv.focusedAppDelegate.requestMaximize()
220 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximized
224 id: maximizeWindowLeftShortcut
225 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left
226 onTriggered: priv.focusedAppDelegate.requestMaximizeLeft()
227 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight
231 id: maximizeWindowRightShortcut
232 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right
233 onTriggered: priv.focusedAppDelegate.requestMaximizeRight()
234 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight
238 id: minimizeRestoreShortcut
239 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
241 if (priv.focusedAppDelegate.anyMaximized) {
242 priv.focusedAppDelegate.requestRestore();
244 priv.focusedAppDelegate.requestMinimize();
247 active: root.state == "windowed" && priv.focusedAppDelegate
251 shortcut: Qt.AltModifier|Qt.Key_Print
252 onTriggered: root.itemSnapshotRequested(priv.focusedAppDelegate)
253 active: priv.focusedAppDelegate !== null
257 shortcut: Qt.ControlModifier|Qt.AltModifier|Qt.Key_T
259 // try in this order: snap pkg, new deb name, old deb name
260 var candidates = ["ubuntu-terminal-app_ubuntu-terminal-app", "ubuntu-terminal-app", "com.ubuntu.terminal_terminal"];
261 for (var i = 0; i < candidates.length; i++) {
262 if (priv.startApp(candidates[i]))
270 objectName: "DesktopStagePrivate"
272 function startApp(appId) {
273 if (root.applicationManager.findApplication(appId)) {
274 return root.applicationManager.requestFocusApplication(appId);
276 return root.applicationManager.startApplication(appId) !== null;
280 property var focusedAppDelegate: null
281 property var foregroundMaximizedAppDelegate: null // for stuff like drop shadow and focusing maximized app by clicking panel
283 property bool goneToSpread: false
284 property int closingIndex: -1
285 property int animationDuration: UbuntuAnimation.FastDuration
287 function updateForegroundMaximizedApp() {
289 for (var i = 0; i < appRepeater.count && !found; i++) {
290 var item = appRepeater.itemAt(i);
291 if (item && item.visuallyMaximized) {
292 foregroundMaximizedAppDelegate = item;
297 foregroundMaximizedAppDelegate = null;
301 function minimizeAllWindows() {
302 for (var i = appRepeater.count - 1; i >= 0; i--) {
303 var appDelegate = appRepeater.itemAt(i);
304 if (appDelegate && !appDelegate.minimized) {
305 appDelegate.requestMinimize();
310 readonly property bool sideStageEnabled: root.mode === "stagedWithSideStage" &&
311 (root.shellOrientation == Qt.LandscapeOrientation ||
312 root.shellOrientation == Qt.InvertedLandscapeOrientation)
313 onSideStageEnabledChanged: {
314 for (var i = 0; i < appRepeater.count; i++) {
315 appRepeater.itemAt(i).refreshStage();
317 priv.updateMainAndSideStageIndexes();
320 property var mainStageDelegate: null
321 property var sideStageDelegate: null
322 property int mainStageItemId: 0
323 property int sideStageItemId: 0
324 property string mainStageAppId: ""
325 property string sideStageAppId: ""
327 onSideStageDelegateChanged: {
328 if (!sideStageDelegate) {
333 function updateMainAndSideStageIndexes() {
334 if (root.mode != "stagedWithSideStage") {
335 priv.sideStageDelegate = null;
336 priv.sideStageItemId = 0;
337 priv.sideStageAppId = "";
338 priv.mainStageDelegate = appRepeater.itemAt(0);
339 priv.mainStageItemId = topLevelSurfaceList.idAt(0);
340 priv.mainStageAppId = topLevelSurfaceList.applicationAt(0) ? topLevelSurfaceList.applicationAt(0).appId : ""
344 var choseMainStage = false;
345 var choseSideStage = false;
347 if (!root.topLevelSurfaceList)
350 for (var i = 0; i < appRepeater.count && (!choseMainStage || !choseSideStage); ++i) {
351 var appDelegate = appRepeater.itemAt(i);
353 // This might happen during startup phase... If the delegate appears and claims focus
354 // things are updated and appRepeater.itemAt(x) still returns null while appRepeater.count >= x
355 // Lets just skip it, on startup it will be generated at a later point too...
358 if (sideStage.shown && appDelegate.stage == ApplicationInfoInterface.SideStage
359 && !choseSideStage) {
360 priv.sideStageDelegate = appDelegate
361 priv.sideStageItemId = root.topLevelSurfaceList.idAt(i);
362 priv.sideStageAppId = root.topLevelSurfaceList.applicationAt(i).appId;
363 choseSideStage = true;
364 } else if (!choseMainStage && appDelegate.stage == ApplicationInfoInterface.MainStage) {
365 priv.mainStageDelegate = appDelegate;
366 priv.mainStageItemId = root.topLevelSurfaceList.idAt(i);
367 priv.mainStageAppId = root.topLevelSurfaceList.applicationAt(i).appId;
368 choseMainStage = true;
371 if (!choseMainStage && priv.mainStageDelegate) {
372 priv.mainStageDelegate = null;
373 priv.mainStageItemId = 0;
374 priv.mainStageAppId = "";
376 if (!choseSideStage && priv.sideStageDelegate) {
377 priv.sideStageDelegate = null;
378 priv.sideStageItemId = 0;
379 priv.sideStageAppId = "";
383 property int nextInStack: {
384 var mainStageIndex = priv.mainStageDelegate ? priv.mainStageDelegate.itemIndex : -1;
385 var sideStageIndex = priv.sideStageDelegate ? priv.sideStageDelegate.itemIndex : -1;
386 if (sideStageIndex == -1) {
387 return topLevelSurfaceList.count > 1 ? 1 : -1;
389 if (mainStageIndex == 0 || sideStageIndex == 0) {
390 if (mainStageIndex == 1 || sideStageIndex == 1) {
391 return topLevelSurfaceList.count > 2 ? 2 : -1;
398 readonly property real virtualKeyboardHeight: root.inputMethodRect.height
400 readonly property real windowDecorationHeight: units.gu(3)
403 Component.onCompleted: priv.updateMainAndSideStageIndexes();
407 onCloseClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
408 onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestMinimize(); } }
409 onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestRestore(); } }
414 property: "decorationsVisible"
415 value: mode == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.maximized && !root.spreadShown
422 if (priv.focusedAppDelegate !== null) {
423 if (priv.focusedAppDelegate.maximized)
424 return priv.focusedAppDelegate.title
426 return priv.focusedAppDelegate.appName
430 when: priv.focusedAppDelegate
435 property: "focusedPersistentSurfaceId"
437 if (priv.focusedAppDelegate !== null) {
438 if (priv.focusedAppDelegate.surface) {
439 return priv.focusedAppDelegate.surface.persistentId;
444 when: priv.focusedAppDelegate
449 property: "dropShadow"
450 value: priv.focusedAppDelegate && !priv.focusedAppDelegate.maximized && priv.foregroundMaximizedAppDelegate !== null && mode == "windowed"
455 property: "closeButtonShown"
456 value: priv.focusedAppDelegate && priv.focusedAppDelegate.maximized
459 Component.onDestruction: {
460 PanelState.title = "";
461 PanelState.decorationsVisible = false;
462 PanelState.dropShadow = false;
466 model: root.applicationManager
468 property var stateBinding: Binding {
469 target: model.application
470 property: "requestedState"
472 // TODO: figure out some lifecycle policy, like suspending minimized apps
473 // or something if running windowed.
474 // TODO: If the device has a dozen suspended apps because it was running
475 // in staged mode, when it switches to Windowed mode it will suddenly
476 // resume all those apps at once. We might want to avoid that.
477 value: root.mode === "windowed"
478 || (!root.suspended && model.application && priv.focusedAppDelegate &&
479 (priv.focusedAppDelegate.appId === model.application.appId ||
480 priv.mainStageAppId === model.application.appId ||
481 priv.sideStageAppId === model.application.appId))
482 ? ApplicationInfoInterface.RequestedRunning
483 : ApplicationInfoInterface.RequestedSuspended
486 property var lifecycleBinding: Binding {
487 target: model.application
488 property: "exemptFromLifecycle"
489 value: model.application
490 ? (!model.application.isTouchApp || isExemptFromLifecycle(model.application.appId))
494 property var focusRequestedConnection: Connections {
495 target: model.application
498 // Application emits focusRequested when it has no surface (i.e. their processes died).
499 // Find the topmost window for this application and activate it, after which the app
500 // will be requested to be running.
502 for (var i = 0; i < appRepeater.count; i++) {
503 var appDelegate = appRepeater.itemAt(i);
504 if (appDelegate.application.appId === model.application.appId) {
505 appDelegate.activate();
510 console.warn("Application requested te be focused but no window for it. What should we do?");
518 name: "spread"; when: priv.goneToSpread
519 PropertyChanges { target: floatingFlickable; enabled: true }
520 PropertyChanges { target: root; focus: true }
521 PropertyChanges { target: spreadItem; focus: true }
522 PropertyChanges { target: hoverMouseArea; enabled: true }
523 PropertyChanges { target: rightEdgeDragArea; enabled: false }
524 PropertyChanges { target: cancelSpreadMouseArea; enabled: true }
525 PropertyChanges { target: noAppsRunningHint; visible: (root.topLevelSurfaceList.count < 1) }
526 PropertyChanges { target: blurLayer; visible: true; blurRadius: 32; brightness: .65; opacity: 1 }
527 PropertyChanges { target: wallpaper; visible: false }
530 name: "stagedRightEdge"; when: root.spreadEnabled && (rightEdgeDragArea.dragging || rightEdgePushProgress > 0) && root.mode == "staged"
538 PropertyChanges { target: noAppsRunningHint; visible: (root.topLevelSurfaceList.count < 1) }
541 name: "sideStagedRightEdge"; when: root.spreadEnabled && (rightEdgeDragArea.dragging || rightEdgePushProgress > 0) && root.mode == "stagedWithSideStage"
542 extend: "stagedRightEdge"
545 opacity: priv.sideStageDelegate && priv.sideStageDelegate.x === sideStage.x ? 1 : 0
550 name: "windowedRightEdge"; when: root.spreadEnabled && (rightEdgeDragArea.dragging || rightEdgePushProgress > 0) && root.mode == "windowed"
556 opacity: MathUtils.linearAnimation(spreadItem.rightEdgeBreakPoint, 1, 0, 1, Math.max(rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0, rightEdgePushProgress))
560 name: "staged"; when: root.mode === "staged"
561 PropertyChanges { target: wallpaper; visible: !priv.focusedAppDelegate || priv.focusedAppDelegate.x !== 0 }
562 PropertyChanges { target: root; focus: true }
563 PropertyChanges { target: appContainer; focus: true }
566 name: "stagedWithSideStage"; when: root.mode === "stagedWithSideStage"
567 PropertyChanges { target: triGestureArea; enabled: priv.sideStageEnabled }
568 PropertyChanges { target: sideStage; visible: true }
569 PropertyChanges { target: root; focus: true }
570 PropertyChanges { target: appContainer; focus: true }
573 name: "windowed"; when: root.mode === "windowed"
574 PropertyChanges { target: root; focus: true }
575 PropertyChanges { target: appContainer; focus: true }
580 from: "stagedRightEdge,sideStagedRightEdge,windowedRightEdge"; to: "spread"
581 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
582 PropertyAnimation { target: blurLayer; properties: "brightness,blurRadius"; duration: priv.animationDuration }
586 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: appRepeater.count > 1 ? 1 : 0 }
590 SequentialAnimation {
593 var item = appRepeater.itemAt(Math.max(0, spreadItem.highlightedIndex));
594 if (item.stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
597 item.playFocusAnimation();
600 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
601 PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
605 to: "stagedRightEdge,sideStagedRightEdge"
606 PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
609 to: "stagedWithSideStage"
610 ScriptAction { script: priv.updateMainAndSideStageIndexes(); }
616 id: cancelSpreadMouseArea
619 onClicked: priv.goneToSpread = false
624 objectName: "appContainer"
630 objectName: "stageBackground"
632 source: root.background
633 // Make sure it's the lowest item. Due to the left edge drag we sometimes need
634 // to put the dash at -1 and we don't want it behind the Wallpaper
647 objectName: "spreadItem"
648 anchors.fill: appContainer
649 leftMargin: root.availableDesktopArea.x
650 model: root.topLevelSurfaceList
651 spreadFlickable: floatingFlickable
655 priv.goneToSpread = false;
659 appRepeater.itemAt(highlightedIndex).close();
664 id: noAppsRunningHint
666 anchors.horizontalCenter: parent.horizontalCenter
667 anchors.verticalCenter: parent.verticalCenter
669 horizontalAlignment: Qt.AlignHCenter
670 verticalAlignment: Qt.AlignVCenter
671 anchors.leftMargin: root.launcherLeftMargin
672 wrapMode: Label.WordWrap
674 text: i18n.tr("No running apps")
678 target: root.topLevelSurfaceList
679 onListChanged: priv.updateMainAndSideStageIndexes()
684 objectName: "MainStageDropArea"
688 bottom: parent.bottom
690 width: appContainer.width - sideStage.width
691 enabled: priv.sideStageEnabled
694 drop.source.appDelegate.saveStage(ApplicationInfoInterface.MainStage);
695 drop.source.appDelegate.focus = true;
702 objectName: "sideStage"
704 height: appContainer.height
705 x: appContainer.width - width
707 Behavior on opacity { UbuntuNumberAnimation {} }
709 if (!priv.mainStageItemId) return 0;
711 if (priv.sideStageItemId && priv.nextInStack > 0) {
713 // Due the order in which bindings are evaluated, this might be triggered while shuffling
714 // the list and index doesn't yet match with itemIndex (even though itemIndex: index)
715 // Let's walk the list and compare itemIndex to make sure we have the correct one.
716 var nextDelegateInStack = -1;
717 for (var i = 0; i < appRepeater.count; i++) {
718 if (appRepeater.itemAt(i).itemIndex == priv.nextInStack) {
719 nextDelegateInStack = appRepeater.itemAt(i);
724 if (nextDelegateInStack.stage === ApplicationInfoInterface.MainStage) {
725 // if the next app in stack is a main stage app, put the sidestage on top of it.
735 if (!shown && priv.mainStageDelegate && !root.spreadShown) {
736 priv.mainStageDelegate.activate();
741 id: sideStageDropArea
742 objectName: "SideStageDropArea"
745 property bool dropAllowed: true
748 dropAllowed = drag.keys != "Disabled";
754 if (drop.keys == "MainStage") {
755 drop.source.appDelegate.saveStage(ApplicationInfoInterface.SideStage);
756 drop.source.appDelegate.focus = true;
761 if (!sideStageDropArea.drag.source) {
771 model: topLevelSurfaceList
772 objectName: "appRepeater"
774 function indexOf(delegateItem) {
775 for (var i = 0; i < count; i++) {
776 if (itemAt(i) === delegateItem) {
783 delegate: FocusScope {
785 objectName: "appDelegate_" + model.window.id
786 property int itemIndex: index // We need this from outside the repeater
787 // z might be overriden in some cases by effects, but we need z ordering
788 // to calculate occlusion detection
789 property int normalZ: topLevelSurfaceList.count - index
791 if (visuallyMaximized) {
792 priv.updateForegroundMaximizedApp();
797 // Set these as propertyes as they wont update otherwise
798 property real screenOffsetX: Screen.virtualX
799 property real screenOffsetY: Screen.virtualY
801 // Normally we want x/y where the surface thinks it is. Width/height of our delegate will
802 // match what the actual surface size is.
803 // Don't write to those, they will be set by states
805 // Here we will also need to remove the screen offset from miral's results
806 // as unity8 x,y will be relative to the current screen only
807 // FIXME: when proper multiscreen lands
808 x: model.window.position.x - clientAreaItem.x - screenOffsetX
809 y: model.window.position.y - clientAreaItem.y - screenOffsetY
810 width: decoratedWindow.implicitWidth
811 height: decoratedWindow.implicitHeight
813 // requestedX/Y/width/height is what we ask the actual surface to be.
814 // Do not write to those, they will be set by states
815 property real requestedX: windowedX
816 property real requestedY: windowedY
817 property real requestedWidth: windowedWidth
818 property real requestedHeight: windowedHeight
820 // For both windowed and staged need to tell miral what screen we are on,
821 // so we need to add the screen offset to the position we tell miral
822 // FIXME: when proper multiscreen lands
824 target: model.window; property: "requestedPosition"
825 // miral doesn't know about our window decorations. So we have to deduct them
826 value: Qt.point(appDelegate.requestedX + appDelegate.clientAreaItem.x + screenOffsetX,
827 appDelegate.requestedY + appDelegate.clientAreaItem.y + screenOffsetY)
828 when: root.mode == "windowed"
831 target: model.window; property: "requestedPosition"
832 value: Qt.point(screenOffsetX, screenOffsetY)
833 when: root.mode != "windowed"
836 // In those are for windowed mode. Those values basically store the window's properties
837 // when having a floating window. If you want to move/resize a window in normal mode, this is what you want to write to.
838 property real windowedX
839 property real windowedY
840 property real windowedWidth
841 property real windowedHeight
843 // unlike windowedX/Y, this is the last known grab position before being pushed against edges/corners
844 // when restoring, the window should return to these, not to the place where it was dropped near the edge
845 property real restoredX
846 property real restoredY
848 // Keeps track of the window geometry while in normal or restored state
849 // Useful when returning from some maxmized state or when saving the geometry while maximized
850 // FIXME: find a better solution
851 property real normalX: 0
852 property real normalY: 0
853 property real normalWidth: 0
854 property real normalHeight: 0
855 function updateNormalGeometry() {
856 if (appDelegate.state == "normal" || appDelegate.state == "restored") {
857 normalX = appDelegate.requestedX;
858 normalY = appDelegate.requestedY;
859 normalWidth = appDelegate.width;
860 normalHeight = appDelegate.height;
863 function updateRestoredGeometry() {
864 if (appDelegate.state == "normal" || appDelegate.state == "restored") {
865 // save the x/y to restore to
866 restoredX = appDelegate.x;
867 restoredY = appDelegate.y;
873 onXChanged: appDelegate.updateNormalGeometry();
874 onYChanged: appDelegate.updateNormalGeometry();
875 onWidthChanged: appDelegate.updateNormalGeometry();
876 onHeightChanged: appDelegate.updateNormalGeometry();
879 // True when the Stage is focusing this app and playing its own animation.
880 // Stays true until the app is unfocused.
881 // If it is, we don't want to play the slide in/out transition from StageMaths.
882 // Setting it imperatively is not great, but any declarative solution hits
883 // race conditions, causing two animations to play for one focus event.
884 property bool inhibitSlideAnimation: false
889 value: appDelegate.requestedY -
890 Math.min(appDelegate.requestedY - root.availableDesktopArea.y,
891 Math.max(0, priv.virtualKeyboardHeight - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
892 when: root.oskEnabled && appDelegate.focus && (appDelegate.state == "normal" || appDelegate.state == "restored")
893 && root.inputMethodRect.height > 0
896 Behavior on x { id: xBehavior; enabled: priv.closingIndex >= 0; UbuntuNumberAnimation { onRunningChanged: if (!running) priv.closingIndex = -1} }
900 onShellOrientationAngleChanged: {
901 // at this point decoratedWindow.surfaceOrientationAngle is the old shellOrientationAngle
902 if (application && application.rotatesWindowContents) {
903 if (root.state == "windowed") {
904 var angleDiff = decoratedWindow.surfaceOrientationAngle - shellOrientationAngle;
905 angleDiff = (360 + angleDiff) % 360;
906 if (angleDiff === 90 || angleDiff === 270) {
907 var aux = decoratedWindow.requestedHeight;
908 decoratedWindow.requestedHeight = decoratedWindow.requestedWidth + decoratedWindow.actualDecorationHeight;
909 decoratedWindow.requestedWidth = aux - decoratedWindow.actualDecorationHeight;
912 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
914 decoratedWindow.surfaceOrientationAngle = 0;
919 readonly property alias application: decoratedWindow.application
920 readonly property alias minimumWidth: decoratedWindow.minimumWidth
921 readonly property alias minimumHeight: decoratedWindow.minimumHeight
922 readonly property alias maximumWidth: decoratedWindow.maximumWidth
923 readonly property alias maximumHeight: decoratedWindow.maximumHeight
924 readonly property alias widthIncrement: decoratedWindow.widthIncrement
925 readonly property alias heightIncrement: decoratedWindow.heightIncrement
927 readonly property bool maximized: windowState === WindowStateStorage.WindowStateMaximized
928 readonly property bool maximizedLeft: windowState === WindowStateStorage.WindowStateMaximizedLeft
929 readonly property bool maximizedRight: windowState === WindowStateStorage.WindowStateMaximizedRight
930 readonly property bool maximizedHorizontally: windowState === WindowStateStorage.WindowStateMaximizedHorizontally
931 readonly property bool maximizedVertically: windowState === WindowStateStorage.WindowStateMaximizedVertically
932 readonly property bool maximizedTopLeft: windowState === WindowStateStorage.WindowStateMaximizedTopLeft
933 readonly property bool maximizedTopRight: windowState === WindowStateStorage.WindowStateMaximizedTopRight
934 readonly property bool maximizedBottomLeft: windowState === WindowStateStorage.WindowStateMaximizedBottomLeft
935 readonly property bool maximizedBottomRight: windowState === WindowStateStorage.WindowStateMaximizedBottomRight
936 readonly property bool anyMaximized: maximized || maximizedLeft || maximizedRight || maximizedHorizontally || maximizedVertically ||
937 maximizedTopLeft || maximizedTopRight || maximizedBottomLeft || maximizedBottomRight
939 readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
940 readonly property bool fullscreen: windowState === WindowStateStorage.WindowStateFullscreen
942 readonly property bool canBeMaximized: canBeMaximizedHorizontally && canBeMaximizedVertically
943 readonly property bool canBeMaximizedLeftRight: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
944 (maximumHeight == 0 || maximumHeight >= appContainer.height)
945 readonly property bool canBeCornerMaximized: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
946 (maximumHeight == 0 || maximumHeight >= appContainer.height/2)
947 readonly property bool canBeMaximizedHorizontally: maximumWidth == 0 || maximumWidth >= appContainer.width
948 readonly property bool canBeMaximizedVertically: maximumHeight == 0 || maximumHeight >= appContainer.height
949 readonly property alias orientationChangesEnabled: decoratedWindow.orientationChangesEnabled
951 // TODO drop our own windowType once Mir/Miral/Qtmir gets in sync with ours
952 property int windowState: WindowStateStorage.WindowStateNormal
953 property int prevWindowState: WindowStateStorage.WindowStateRestored
955 property bool animationsEnabled: true
956 property alias title: decoratedWindow.title
957 readonly property string appName: model.application ? model.application.name : ""
958 property bool visuallyMaximized: false
959 property bool visuallyMinimized: false
960 readonly property alias windowedTransitionRunning: windowedTransition.running
962 property int stage: ApplicationInfoInterface.MainStage
963 function saveStage(newStage) {
964 appDelegate.stage = newStage;
965 WindowStateStorage.saveStage(appId, newStage);
966 priv.updateMainAndSideStageIndexes()
969 readonly property var surface: model.window.surface
970 readonly property var window: model.window
972 readonly property alias focusedSurface: decoratedWindow.focusedSurface
973 readonly property bool dragging: touchControls.overlayShown ? touchControls.dragging : decoratedWindow.dragging
975 readonly property string appId: model.application.appId
976 readonly property alias clientAreaItem: decoratedWindow.clientAreaItem
978 function activate() {
979 if (model.window.focused) {
980 updateQmlFocusFromMirSurfaceFocus();
982 model.window.activate();
985 function requestMaximize() { model.window.requestState(Mir.MaximizedState); }
986 function requestMaximizeVertically() { model.window.requestState(Mir.VertMaximizedState); }
987 function requestMaximizeHorizontally() { model.window.requestState(Mir.HorizMaximizedState); }
988 function requestMaximizeLeft() { model.window.requestState(Mir.MaximizedLeftState); }
989 function requestMaximizeRight() { model.window.requestState(Mir.MaximizedRightState); }
990 function requestMaximizeTopLeft() { model.window.requestState(Mir.MaximizedTopLeftState); }
991 function requestMaximizeTopRight() { model.window.requestState(Mir.MaximizedTopRightState); }
992 function requestMaximizeBottomLeft() { model.window.requestState(Mir.MaximizedBottomLeftState); }
993 function requestMaximizeBottomRight() { model.window.requestState(Mir.MaximizedBottomRightState); }
994 function requestMinimize() { model.window.requestState(Mir.MinimizedState); }
995 function requestRestore() { model.window.requestState(Mir.RestoredState); }
997 function claimFocus() {
998 if (root.state == "spread") {
999 spreadItem.highlightedIndex = index
1000 priv.goneToSpread = false;
1002 if (root.mode == "stagedWithSideStage") {
1003 if (appDelegate.stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
1006 priv.updateMainAndSideStageIndexes();
1008 appDelegate.focus = true;
1010 // Don't set focusedAppDelegate (and signal mainAppChanged) unnecessarily
1011 // which can happen after getting interactive again.
1012 if (priv.focusedAppDelegate !== appDelegate)
1013 priv.focusedAppDelegate = appDelegate;
1016 function updateQmlFocusFromMirSurfaceFocus() {
1017 if (model.window.focused) {
1019 decoratedWindow.focus = true;
1024 id: windowStateSaver
1026 screenWidth: appContainer.width
1027 screenHeight: appContainer.height
1028 leftMargin: root.availableDesktopArea.x
1029 minimumY: root.availableDesktopArea.y
1033 target: model.window
1035 updateQmlFocusFromMirSurfaceFocus();
1036 if (!model.window.focused) {
1037 inhibitSlideAnimation = false;
1041 appDelegate.activate();
1044 if (value == Mir.MinimizedState) {
1045 appDelegate.minimize();
1046 } else if (value == Mir.MaximizedState) {
1047 appDelegate.maximize();
1048 } else if (value == Mir.VertMaximizedState) {
1049 appDelegate.maximizeVertically();
1050 } else if (value == Mir.HorizMaximizedState) {
1051 appDelegate.maximizeHorizontally();
1052 } else if (value == Mir.MaximizedLeftState) {
1053 appDelegate.maximizeLeft();
1054 } else if (value == Mir.MaximizedRightState) {
1055 appDelegate.maximizeRight();
1056 } else if (value == Mir.MaximizedTopLeftState) {
1057 appDelegate.maximizeTopLeft();
1058 } else if (value == Mir.MaximizedTopRightState) {
1059 appDelegate.maximizeTopRight();
1060 } else if (value == Mir.MaximizedBottomLeftState) {
1061 appDelegate.maximizeBottomLeft();
1062 } else if (value == Mir.MaximizedBottomRightState) {
1063 appDelegate.maximizeBottomRight();
1064 } else if (value == Mir.RestoredState) {
1065 if (appDelegate.fullscreen && appDelegate.prevWindowState != WindowStateStorage.WindowStateRestored
1066 && appDelegate.prevWindowState != WindowStateStorage.WindowStateNormal) {
1067 model.window.requestState(WindowStateStorage.toMirState(appDelegate.prevWindowState));
1069 appDelegate.restore();
1071 } else if (value == Mir.FullscreenState) {
1072 appDelegate.prevWindowState = appDelegate.windowState;
1073 appDelegate.windowState = WindowStateStorage.WindowStateFullscreen;
1078 readonly property bool windowReady: clientAreaItem.surfaceInitialized
1079 onWindowReadyChanged: {
1081 var loadedMirState = WindowStateStorage.toMirState(windowStateSaver.loadedState);
1082 var state = loadedMirState;
1084 if (window.state == Mir.FullscreenState) {
1085 // If the app is fullscreen at startup, we should not use saved state
1086 // Example of why: if you open game that only requests fullscreen at
1087 // Statup, this will automaticly be set to "restored state" since
1088 // thats the default value of stateStorage, this will result in the app
1089 // having the "restored state" as it will not make a fullscreen
1090 // call after the app has started.
1091 console.log("Inital window state is fullscreen, not using saved state.");
1092 state = window.state;
1093 } else if (loadedMirState == Mir.FullscreenState) {
1094 // If saved state is fullscreen, we should use app inital state
1095 // Example of why: if you open browser with youtube video at fullscreen
1096 // and close this app, it will be fullscreen next time you open the app.
1097 console.log("Saved window state is fullscreen, using inital window state");
1098 state = window.state;
1101 // need to apply the shell chrome policy on top the saved window state
1103 if (root.mode == "windowed") {
1104 policy = windowedFullscreenPolicy;
1106 policy = stagedFullscreenPolicy
1108 window.requestState(policy.applyPolicy(state, surface.shellChrome));
1112 Component.onCompleted: {
1113 if (application && application.rotatesWindowContents) {
1114 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
1116 decoratedWindow.surfaceOrientationAngle = 0;
1119 // First, cascade the newly created window, relative to the currently/old focused window.
1120 windowedX = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedX + units.gu(3) : (normalZ - 1) * units.gu(3)
1121 windowedY = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedY + units.gu(3) : normalZ * units.gu(3)
1122 // Now load any saved state. This needs to happen *after* the cascading!
1123 windowStateSaver.load();
1125 updateQmlFocusFromMirSurfaceFocus();
1128 _constructing = false;
1130 Component.onDestruction: {
1131 windowStateSaver.save();
1134 // This stage is about to be destroyed. Don't mess up with the model at this point
1138 if (visuallyMaximized) {
1139 priv.updateForegroundMaximizedApp();
1143 onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
1145 property bool _constructing: true;
1147 if (!_constructing) {
1148 priv.updateMainAndSideStageIndexes();
1154 && !greeter.fullyShown
1155 && (priv.foregroundMaximizedAppDelegate === null || priv.foregroundMaximizedAppDelegate.normalZ <= z)
1157 || appDelegate.fullscreen
1158 || focusAnimation.running || rightEdgeFocusAnimation.running || hidingAnimation.running
1161 model.window.close();
1164 function maximize(animated) {
1165 animationsEnabled = (animated === undefined) || animated;
1166 windowState = WindowStateStorage.WindowStateMaximized;
1168 function maximizeLeft(animated) {
1169 animationsEnabled = (animated === undefined) || animated;
1170 windowState = WindowStateStorage.WindowStateMaximizedLeft;
1172 function maximizeRight(animated) {
1173 animationsEnabled = (animated === undefined) || animated;
1174 windowState = WindowStateStorage.WindowStateMaximizedRight;
1176 function maximizeHorizontally(animated) {
1177 animationsEnabled = (animated === undefined) || animated;
1178 windowState = WindowStateStorage.WindowStateMaximizedHorizontally;
1180 function maximizeVertically(animated) {
1181 animationsEnabled = (animated === undefined) || animated;
1182 windowState = WindowStateStorage.WindowStateMaximizedVertically;
1184 function maximizeTopLeft(animated) {
1185 animationsEnabled = (animated === undefined) || animated;
1186 windowState = WindowStateStorage.WindowStateMaximizedTopLeft;
1188 function maximizeTopRight(animated) {
1189 animationsEnabled = (animated === undefined) || animated;
1190 windowState = WindowStateStorage.WindowStateMaximizedTopRight;
1192 function maximizeBottomLeft(animated) {
1193 animationsEnabled = (animated === undefined) || animated;
1194 windowState = WindowStateStorage.WindowStateMaximizedBottomLeft;
1196 function maximizeBottomRight(animated) {
1197 animationsEnabled = (animated === undefined) || animated;
1198 windowState = WindowStateStorage.WindowStateMaximizedBottomRight;
1200 function minimize(animated) {
1201 animationsEnabled = (animated === undefined) || animated;
1202 windowState |= WindowStateStorage.WindowStateMinimized; // add the minimized bit
1204 function restore(animated,state) {
1205 animationsEnabled = (animated === undefined) || animated;
1206 windowState = state || WindowStateStorage.WindowStateRestored;
1207 windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
1208 prevWindowState = windowState;
1211 function playFocusAnimation() {
1212 if (state == "stagedRightEdge") {
1213 // TODO: Can we drop this if and find something that always works?
1214 if (root.mode == "staged") {
1215 rightEdgeFocusAnimation.targetX = 0
1216 rightEdgeFocusAnimation.start()
1217 } else if (root.mode == "stagedWithSideStage") {
1218 rightEdgeFocusAnimation.targetX = appDelegate.stage == ApplicationInfoInterface.SideStage ? sideStage.x : 0
1219 rightEdgeFocusAnimation.start()
1221 } else if (state == "windowedRightEdge" || state == "windowed") {
1224 focusAnimation.start()
1227 function playHidingAnimation() {
1228 if (state != "windowedRightEdge") {
1229 hidingAnimation.start()
1233 function refreshStage() {
1234 var newStage = ApplicationInfoInterface.MainStage;
1235 if (priv.sideStageEnabled) { // we're in lanscape rotation.
1236 if (application && application.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {
1237 var defaultStage = ApplicationInfoInterface.SideStage; // if application supports portrait, it defaults to sidestage.
1238 if (application.supportedOrientations & (Qt.LandscapeOrientation|Qt.InvertedLandscapeOrientation)) {
1239 // if it supports lanscape, it defaults to mainstage.
1240 defaultStage = ApplicationInfoInterface.MainStage;
1242 newStage = WindowStateStorage.getStage(application.appId, defaultStage);
1247 if (focus && stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
1252 UbuntuNumberAnimation {
1258 duration: UbuntuAnimation.SnapDuration
1260 topLevelSurfaceList.pendingActivation();
1261 topLevelSurfaceList.raiseId(model.window.id);
1264 appDelegate.activate();
1268 id: rightEdgeFocusAnimation
1269 property int targetX: 0
1270 UbuntuNumberAnimation { target: appDelegate; properties: "x"; to: rightEdgeFocusAnimation.targetX; duration: priv.animationDuration }
1271 UbuntuNumberAnimation { target: decoratedWindow; properties: "angle"; to: 0; duration: priv.animationDuration }
1272 UbuntuNumberAnimation { target: decoratedWindow; properties: "itemScale"; to: 1; duration: priv.animationDuration }
1274 topLevelSurfaceList.pendingActivation();
1275 inhibitSlideAnimation = true;
1278 appDelegate.activate();
1283 UbuntuNumberAnimation { target: appDelegate; property: "opacity"; to: 0; duration: priv.animationDuration }
1284 onStopped: appDelegate.opacity = 1
1291 flickable: floatingFlickable
1295 sceneWidth: root.width
1296 stage: appDelegate.stage
1297 thisDelegate: appDelegate
1298 mainStageDelegate: priv.mainStageDelegate
1299 sideStageDelegate: priv.sideStageDelegate
1300 sideStageWidth: sideStage.panelWidth
1301 sideStageHandleWidth: sideStage.handleWidth
1302 sideStageX: sideStage.x
1303 itemIndex: appDelegate.itemIndex
1304 nextInStack: priv.nextInStack
1305 animationDuration: priv.animationDuration
1308 StagedRightEdgeMaths {
1309 id: stagedRightEdgeMaths
1310 sceneWidth: root.availableDesktopArea.width
1311 sceneHeight: appContainer.height
1312 isMainStageApp: priv.mainStageDelegate == appDelegate
1313 isSideStageApp: priv.sideStageDelegate == appDelegate
1314 sideStageWidth: sideStage.width
1315 sideStageOpen: sideStage.shown
1317 nextInStack: priv.nextInStack
1319 targetHeight: spreadItem.stackHeight
1320 targetX: spreadMaths.targetX
1321 startY: appDelegate.fullscreen ? 0 : root.availableDesktopArea.y
1322 targetY: spreadMaths.targetY
1323 targetAngle: spreadMaths.targetAngle
1324 targetScale: spreadMaths.targetScale
1325 shuffledZ: stageMaths.itemZ
1326 breakPoint: spreadItem.rightEdgeBreakPoint
1329 WindowedRightEdgeMaths {
1330 id: windowedRightEdgeMaths
1332 startWidth: appDelegate.requestedWidth
1333 startHeight: appDelegate.requestedHeight
1334 targetHeight: spreadItem.stackHeight
1335 targetX: spreadMaths.targetX
1336 targetY: spreadMaths.targetY
1337 normalZ: appDelegate.normalZ
1338 targetAngle: spreadMaths.targetAngle
1339 targetScale: spreadMaths.targetScale
1340 breakPoint: spreadItem.rightEdgeBreakPoint
1345 name: "spread"; when: root.state == "spread"
1346 StateChangeScript { script: { decoratedWindow.cancelDrag(); } }
1348 target: decoratedWindow;
1349 showDecoration: false;
1350 angle: spreadMaths.targetAngle
1351 itemScale: spreadMaths.targetScale
1352 scaleToPreviewSize: spreadItem.stackHeight
1353 scaleToPreviewProgress: 1
1354 hasDecoration: root.mode === "windowed"
1355 shadowOpacity: spreadMaths.shadowOpacity
1356 showHighlight: spreadItem.highlightedIndex === index
1357 darkening: spreadItem.highlightedIndex >= 0
1358 anchors.topMargin: dragArea.distance
1362 x: spreadMaths.targetX
1363 y: spreadMaths.targetY
1365 height: spreadItem.spreadItemHeight
1366 requestedWidth: decoratedWindow.oldRequestedWidth
1367 requestedHeight: decoratedWindow.oldRequestedHeight
1368 visible: spreadMaths.itemVisible
1370 PropertyChanges { target: dragArea; enabled: true }
1371 PropertyChanges { target: windowInfoItem; opacity: spreadMaths.tileInfoOpacity; visible: spreadMaths.itemVisible }
1372 PropertyChanges { target: touchControls; enabled: false }
1375 name: "stagedRightEdge"
1376 when: (root.mode == "staged" || root.mode == "stagedWithSideStage") && (root.state == "sideStagedRightEdge" || root.state == "stagedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running)
1378 target: stagedRightEdgeMaths
1379 progress: Math.max(rightEdgePushProgress, rightEdgeDragArea.draggedProgress)
1383 x: stagedRightEdgeMaths.animatedX
1384 y: stagedRightEdgeMaths.animatedY
1385 z: stagedRightEdgeMaths.animatedZ
1386 height: stagedRightEdgeMaths.animatedHeight
1387 requestedWidth: decoratedWindow.oldRequestedWidth
1388 requestedHeight: decoratedWindow.oldRequestedHeight
1389 visible: appDelegate.x < root.width
1392 target: decoratedWindow
1393 hasDecoration: false
1394 angle: stagedRightEdgeMaths.animatedAngle
1395 itemScale: stagedRightEdgeMaths.animatedScale
1396 scaleToPreviewSize: spreadItem.stackHeight
1397 scaleToPreviewProgress: stagedRightEdgeMaths.scaleToPreviewProgress
1400 // make sure it's visible but transparent so it fades in when we transition to spread
1401 PropertyChanges { target: windowInfoItem; opacity: 0; visible: true }
1404 name: "windowedRightEdge"
1405 when: root.mode == "windowed" && (root.state == "windowedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running || rightEdgePushProgress > 0)
1407 target: windowedRightEdgeMaths
1408 swipeProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0
1409 pushProgress: rightEdgePushProgress
1413 x: windowedRightEdgeMaths.animatedX
1414 y: windowedRightEdgeMaths.animatedY
1415 z: windowedRightEdgeMaths.animatedZ
1416 height: stagedRightEdgeMaths.animatedHeight
1417 requestedWidth: decoratedWindow.oldRequestedWidth
1418 requestedHeight: decoratedWindow.oldRequestedHeight
1421 target: decoratedWindow
1422 showDecoration: windowedRightEdgeMaths.decorationHeight
1423 angle: windowedRightEdgeMaths.animatedAngle
1424 itemScale: windowedRightEdgeMaths.animatedScale
1425 scaleToPreviewSize: spreadItem.stackHeight
1426 scaleToPreviewProgress: windowedRightEdgeMaths.scaleToPreviewProgress
1430 target: opacityEffect;
1431 opacityValue: windowedRightEdgeMaths.opacityMask
1432 sourceItem: windowedRightEdgeMaths.opacityMask < 1 ? decoratedWindow : null
1436 name: "staged"; when: root.state == "staged"
1440 y: root.availableDesktopArea.y
1441 requestedWidth: appContainer.width
1442 requestedHeight: root.availableDesktopArea.height
1443 visuallyMaximized: true
1444 visible: appDelegate.x < root.width
1447 target: decoratedWindow
1448 hasDecoration: false
1456 animateX: !focusAnimation.running && !rightEdgeFocusAnimation.running && itemIndex !== spreadItem.highlightedIndex && !inhibitSlideAnimation
1459 target: appDelegate.window
1460 allowClientResize: false
1464 name: "stagedWithSideStage"; when: root.state == "stagedWithSideStage"
1472 y: root.availableDesktopArea.y
1474 requestedWidth: stageMaths.itemWidth
1475 requestedHeight: root.availableDesktopArea.height
1476 visuallyMaximized: true
1477 visible: appDelegate.x < root.width
1480 target: decoratedWindow
1481 hasDecoration: false
1488 target: appDelegate.window
1489 allowClientResize: false
1493 name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
1495 target: appDelegate;
1496 requestedX: root.availableDesktopArea.x;
1498 visuallyMinimized: false;
1499 requestedWidth: root.availableDesktopArea.width;
1500 requestedHeight: appContainer.height;
1502 PropertyChanges { target: touchControls; enabled: true }
1503 PropertyChanges { target: decoratedWindow; windowControlButtonsVisible: false }
1506 name: "fullscreen"; when: appDelegate.fullscreen && !appDelegate.minimized
1508 target: appDelegate;
1511 requestedWidth: appContainer.width;
1512 requestedHeight: appContainer.height;
1514 PropertyChanges { target: decoratedWindow; hasDecoration: false }
1518 when: appDelegate.windowState == WindowStateStorage.WindowStateNormal
1521 visuallyMinimized: false
1523 PropertyChanges { target: touchControls; enabled: true }
1524 PropertyChanges { target: resizeArea; enabled: true }
1525 PropertyChanges { target: decoratedWindow; shadowOpacity: .3; windowControlButtonsVisible: true}
1529 when: appDelegate.windowState == WindowStateStorage.WindowStateRestored
1532 restoreEntryValues: false
1533 target: appDelegate;
1534 windowedX: restoredX;
1535 windowedY: restoredY;
1539 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
1543 windowedX: root.availableDesktopArea.x
1544 windowedY: root.availableDesktopArea.y
1545 windowedWidth: root.availableDesktopArea.width / 2
1546 windowedHeight: root.availableDesktopArea.height
1550 name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
1551 extend: "maximizedLeft"
1553 target: appDelegate;
1554 windowedX: root.availableDesktopArea.x + (root.availableDesktopArea.width / 2)
1558 name: "maximizedTopLeft"; when: appDelegate.maximizedTopLeft && !appDelegate.minimized
1562 windowedX: root.availableDesktopArea.x
1563 windowedY: root.availableDesktopArea.y
1564 windowedWidth: root.availableDesktopArea.width / 2
1565 windowedHeight: root.availableDesktopArea.height / 2
1569 name: "maximizedTopRight"; when: appDelegate.maximizedTopRight && !appDelegate.minimized
1570 extend: "maximizedTopLeft"
1573 windowedX: root.availableDesktopArea.x + (root.availableDesktopArea.width / 2)
1577 name: "maximizedBottomLeft"; when: appDelegate.maximizedBottomLeft && !appDelegate.minimized
1581 windowedX: root.availableDesktopArea.x
1582 windowedY: root.availableDesktopArea.y + (root.availableDesktopArea.height / 2)
1583 windowedWidth: root.availableDesktopArea.width / 2
1584 windowedHeight: root.availableDesktopArea.height / 2
1588 name: "maximizedBottomRight"; when: appDelegate.maximizedBottomRight && !appDelegate.minimized
1589 extend: "maximizedBottomLeft"
1592 windowedX: root.availableDesktopArea.x + (root.availableDesktopArea.width / 2)
1596 name: "maximizedHorizontally"; when: appDelegate.maximizedHorizontally && !appDelegate.minimized
1600 windowedX: root.availableDesktopArea.x; windowedY: windowedY
1601 windowedWidth: root.availableDesktopArea.width; windowedHeight: windowedHeight
1605 name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
1609 windowedX: windowedX; windowedY: root.availableDesktopArea.y
1610 windowedWidth: windowedWidth; windowedHeight: root.availableDesktopArea.height
1614 name: "minimized"; when: appDelegate.minimized
1617 scale: units.gu(5) / appDelegate.width
1619 visuallyMinimized: true
1620 visuallyMaximized: false
1621 x: -appDelegate.width / 2
1628 // These two animate applications into position from Staged to Desktop and back
1630 from: "staged,stagedWithSideStage"
1631 to: "normal,restored,maximized,maximizedHorizontally,maximizedVertically,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedBottomLeft,maximizedTopRight,maximizedBottomRight"
1632 enabled: appDelegate.animationsEnabled
1633 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
1634 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,opacity,requestedWidth,requestedHeight,scale"; duration: priv.animationDuration }
1637 from: "normal,restored,maximized,maximizedHorizontally,maximizedVertically,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedBottomLeft,maximizedTopRight,maximizedBottomRight"
1638 to: "staged,stagedWithSideStage"
1639 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,requestedWidth,requestedHeight"; duration: priv.animationDuration}
1644 // DecoratedWindow wants the scaleToPreviewSize set before enabling scaleToPreview
1645 PropertyAction { target: appDelegate; properties: "z,visible" }
1646 PropertyAction { target: decoratedWindow; property: "scaleToPreviewSize" }
1647 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,height"; duration: priv.animationDuration }
1648 UbuntuNumberAnimation { target: decoratedWindow; properties: "width,height,itemScale,angle,scaleToPreviewProgress"; duration: priv.animationDuration }
1649 UbuntuNumberAnimation { target: windowInfoItem; properties: "opacity"; duration: priv.animationDuration }
1652 from: "normal,staged"; to: "stagedWithSideStage"
1653 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,requestedWidth,requestedHeight"; duration: priv.animationDuration }
1656 to: "windowedRightEdge"
1659 windowedRightEdgeMaths.startX = appDelegate.requestedX
1660 windowedRightEdgeMaths.startY = appDelegate.requestedY
1663 var thisRect = { x: appDelegate.windowedX, y: appDelegate.windowedY, width: appDelegate.requestedWidth, height: appDelegate.requestedHeight }
1664 var otherDelegate = appRepeater.itemAt(0);
1665 var otherRect = { x: otherDelegate.windowedX, y: otherDelegate.windowedY, width: otherDelegate.requestedWidth, height: otherDelegate.requestedHeight }
1666 var intersectionRect = MathUtils.intersectionRect(thisRect, otherRect)
1667 var mappedInterSectionRect = appDelegate.mapFromItem(root, intersectionRect.x, intersectionRect.y)
1668 opacityEffect.maskX = mappedInterSectionRect.x
1669 opacityEffect.maskY = mappedInterSectionRect.y
1670 opacityEffect.maskWidth = intersectionRect.width
1671 opacityEffect.maskHeight = intersectionRect.height
1677 from: "stagedRightEdge"; to: "staged"
1678 enabled: rightEdgeDragArea.cancelled // only transition back to state if the gesture was cancelled, in the other cases we play the focusAnimations.
1679 SequentialAnimation {
1681 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,height,width,scale"; duration: priv.animationDuration }
1682 UbuntuNumberAnimation { target: decoratedWindow; properties: "width,height,itemScale,angle,scaleToPreviewProgress"; duration: priv.animationDuration }
1684 // We need to release scaleToPreviewSize at last
1685 PropertyAction { target: decoratedWindow; property: "scaleToPreviewSize" }
1686 PropertyAction { target: appDelegate; property: "visible" }
1690 from: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1692 SequentialAnimation {
1693 ScriptAction { script: { fakeRectangle.stop(); } }
1694 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1695 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,scale,opacity"; duration: priv.animationDuration }
1696 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1701 to: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1702 SequentialAnimation {
1703 PropertyAction { target: appDelegate; property: "visuallyMinimized,z" }
1705 UbuntuNumberAnimation { target: appDelegate; properties: "x"; from: -appDelegate.width / 2; duration: priv.animationDuration }
1706 UbuntuNumberAnimation { target: appDelegate; properties: "y,opacity"; duration: priv.animationDuration }
1707 UbuntuNumberAnimation { target: appDelegate; properties: "scale"; from: 0; duration: priv.animationDuration }
1709 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1713 id: windowedTransition
1714 from: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen,minimized"
1715 to: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1716 enabled: appDelegate.animationsEnabled
1717 SequentialAnimation {
1718 ScriptAction { script: {
1719 if (appDelegate.visuallyMaximized) visuallyMaximized = false; // maximized before -> going to restored
1722 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1723 UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,windowedX,windowedY,opacity,scale,requestedWidth,requestedHeight,windowedWidth,windowedHeight";
1724 duration: priv.animationDuration }
1725 ScriptAction { script: {
1726 fakeRectangle.stop();
1727 appDelegate.visuallyMaximized = appDelegate.maximized; // reflect the target state
1736 property: "decorationsAlwaysVisible"
1737 value: appDelegate && appDelegate.maximized && touchControls.overlayShown
1742 objectName: "windowResizeArea"
1744 anchors.fill: appDelegate
1746 // workaround so that it chooses the correct resize borders when you drag from a corner ResizeGrip
1747 anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
1750 boundsItem: root.availableDesktopArea
1751 minWidth: units.gu(10)
1752 minHeight: units.gu(10)
1753 borderThickness: units.gu(2)
1758 appDelegate.activate();
1764 objectName: "decoratedWindow"
1765 anchors.left: appDelegate.left
1766 anchors.top: appDelegate.top
1767 application: model.application
1768 surface: model.window.surface
1769 active: model.window.focused
1771 interactive: root.interactive
1773 decorationHeight: priv.windowDecorationHeight
1774 maximizeButtonShown: appDelegate.canBeMaximized
1775 overlayShown: touchControls.overlayShown
1776 width: implicitWidth
1777 height: implicitHeight
1778 highlightSize: windowInfoItem.iconMargin / 2
1779 altDragEnabled: root.mode == "windowed"
1780 boundsItem: root.availableDesktopArea
1782 requestedWidth: appDelegate.requestedWidth
1783 requestedHeight: appDelegate.requestedHeight
1785 property int oldRequestedWidth: -1
1786 property int oldRequestedHeight: -1
1788 onRequestedWidthChanged: oldRequestedWidth = requestedWidth
1789 onRequestedHeightChanged: oldRequestedHeight = requestedHeight
1791 onCloseClicked: { appDelegate.close(); }
1792 onMaximizeClicked: {
1793 if (appDelegate.canBeMaximized) {
1794 appDelegate.anyMaximized ? appDelegate.requestRestore() : appDelegate.requestMaximize();
1797 onMaximizeHorizontallyClicked: {
1798 if (appDelegate.canBeMaximizedHorizontally) {
1799 appDelegate.maximizedHorizontally ? appDelegate.requestRestore() : appDelegate.requestMaximizeHorizontally()
1802 onMaximizeVerticallyClicked: {
1803 if (appDelegate.canBeMaximizedVertically) {
1804 appDelegate.maximizedVertically ? appDelegate.requestRestore() : appDelegate.requestMaximizeVertically()
1807 onMinimizeClicked: { appDelegate.requestMinimize(); }
1808 onDecorationPressed: { appDelegate.activate(); }
1809 onDecorationReleased: fakeRectangle.visible ? fakeRectangle.commit() : appDelegate.updateRestoredGeometry()
1811 property real angle: 0
1812 Behavior on angle { enabled: priv.closingIndex >= 0; UbuntuNumberAnimation {} }
1813 property real itemScale: 1
1814 Behavior on itemScale { enabled: priv.closingIndex >= 0; UbuntuNumberAnimation {} }
1819 origin.y: decoratedWindow.implicitHeight / 2
1820 xScale: decoratedWindow.itemScale
1821 yScale: decoratedWindow.itemScale
1824 origin { x: 0; y: (decoratedWindow.height / 2) }
1825 axis { x: 0; y: 1; z: 0 }
1826 angle: decoratedWindow.angle
1833 anchors.fill: decoratedWindow
1836 WindowControlsOverlay {
1838 anchors.fill: appDelegate
1840 resizeArea: resizeArea
1843 boundsItem: root.availableDesktopArea
1845 onFakeMaximizeAnimationRequested: if (!appDelegate.maximized) fakeRectangle.maximize(amount, true)
1846 onFakeMaximizeLeftAnimationRequested: if (!appDelegate.maximizedLeft) fakeRectangle.maximizeLeft(amount, true)
1847 onFakeMaximizeRightAnimationRequested: if (!appDelegate.maximizedRight) fakeRectangle.maximizeRight(amount, true)
1848 onFakeMaximizeTopLeftAnimationRequested: if (!appDelegate.maximizedTopLeft) fakeRectangle.maximizeTopLeft(amount, true);
1849 onFakeMaximizeTopRightAnimationRequested: if (!appDelegate.maximizedTopRight) fakeRectangle.maximizeTopRight(amount, true);
1850 onFakeMaximizeBottomLeftAnimationRequested: if (!appDelegate.maximizedBottomLeft) fakeRectangle.maximizeBottomLeft(amount, true);
1851 onFakeMaximizeBottomRightAnimationRequested: if (!appDelegate.maximizedBottomRight) fakeRectangle.maximizeBottomRight(amount, true);
1852 onStopFakeAnimation: fakeRectangle.stop();
1853 onDragReleased: fakeRectangle.visible ? fakeRectangle.commit() : appDelegate.updateRestoredGeometry()
1856 WindowedFullscreenPolicy {
1857 id: windowedFullscreenPolicy
1859 StagedFullscreenPolicy {
1860 id: stagedFullscreenPolicy
1861 active: root.mode == "staged" || root.mode == "stagedWithSideStage"
1862 surface: model.window.surface
1865 SpreadDelegateInputArea {
1867 objectName: "dragArea"
1868 anchors.fill: decoratedWindow
1873 spreadItem.highlightedIndex = index;
1874 if (distance == 0) {
1875 priv.goneToSpread = false;
1879 priv.closingIndex = index
1880 model.window.close();
1886 objectName: "windowInfoItem"
1887 anchors { left: parent.left; top: decoratedWindow.bottom; topMargin: units.gu(1) }
1888 title: model.application.name
1889 iconSource: model.application.icon
1890 height: spreadItem.appInfoHeight
1893 visible: opacity > 0
1895 var nextApp = appRepeater.itemAt(index + 1);
1897 return Math.max(iconHeight, nextApp.x - appDelegate.x - units.gu(1))
1899 return appDelegate.width;
1903 spreadItem.highlightedIndex = index;
1904 priv.goneToSpread = false;
1910 objectName: "closeMouseArea"
1911 anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset }
1912 readonly property var mousePos: hoverMouseArea.mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1913 visible: dragArea.distance == 0
1914 && index == spreadItem.highlightedIndex
1915 && mousePos.y < (decoratedWindow.height / 3)
1916 && mousePos.y > -units.gu(4)
1917 && mousePos.x > -units.gu(4)
1918 && mousePos.x < (decoratedWindow.width * 2 / 3)
1923 priv.closingIndex = index;
1924 appDelegate.close();
1928 source: "graphics/window-close.svg"
1929 anchors.fill: closeMouseArea
1930 anchors.margins: units.gu(2)
1931 sourceSize.width: width
1932 sourceSize.height: height
1937 // Group all child windows in this item so that we can fade them out together when going to the spread
1938 // (and fade them in back again when returning from it)
1939 readonly property bool stageOnProperState: root.state === "windowed"
1940 || root.state === "staged"
1941 || root.state === "stagedWithSideStage"
1943 // TODO: Is it worth the extra cost of layering to avoid the opacity artifacts of intersecting children?
1944 // Btw, will involve more than uncommenting the line below as children won't necessarily fit this item's
1945 // geometry. This is just a reference.
1946 //layer.enabled: opacity !== 0.0 && opacity !== 1.0
1948 opacity: stageOnProperState ? 1.0 : 0.0
1949 visible: opacity !== 0.0 // make it transparent to input as well
1950 Behavior on opacity { UbuntuNumberAnimation {} }
1953 id: childWindowRepeater
1954 model: appDelegate.surface ? appDelegate.surface.childSurfaceList : null
1956 delegate: ChildWindowTree {
1957 surface: model.surface
1959 // Account for the displacement caused by window decoration in the top-level surface
1960 // Ie, the top-level surface is not positioned at (0,0) of this ChildWindow's parent (appDelegate)
1961 displacementX: appDelegate.clientAreaItem.x
1962 displacementY: appDelegate.clientAreaItem.y
1964 boundsItem: root.availableDesktopArea
1965 decorationHeight: priv.windowDecorationHeight
1967 z: childWindowRepeater.count - model.index
1971 // some child surface in this tree got focus.
1972 // Ensure we also have it at the top-level hierarchy
1973 appDelegate.claimFocus();
1983 FakeMaximizeDelegate {
1985 target: priv.focusedAppDelegate
1986 leftMargin: root.availableDesktopArea.x
1987 appContainerWidth: appContainer.width
1988 appContainerHeight: appContainer.height
1993 objectName: "hoverMouseArea"
1994 anchors.fill: appContainer
1995 propagateComposedEvents: true
2000 property int scrollAreaWidth: width / 3
2001 property bool progressiveScrollingEnabled: false
2002 property bool wasTouchPress: false
2005 mouse.accepted = false
2007 if (hoverMouseArea.pressed || wasTouchPress) {
2011 // Find the hovered item and mark it active
2012 for (var i = appRepeater.count - 1; i >= 0; i--) {
2013 var appDelegate = appRepeater.itemAt(i);
2014 var mapped = mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
2015 var itemUnder = appDelegate.childAt(mapped.x, mapped.y);
2016 if (itemUnder && (itemUnder.objectName === "dragArea" || itemUnder.objectName === "windowInfoItem" || itemUnder.objectName == "closeMouseArea")) {
2017 spreadItem.highlightedIndex = i;
2022 if (floatingFlickable.contentWidth > floatingFlickable.width) {
2023 var margins = floatingFlickable.width * 0.05;
2025 if (!progressiveScrollingEnabled && mouseX < floatingFlickable.width - scrollAreaWidth) {
2026 progressiveScrollingEnabled = true
2029 // do we need to scroll?
2030 if (mouseX < scrollAreaWidth + margins) {
2031 var progress = Math.min(1, (scrollAreaWidth + margins - mouseX) / (scrollAreaWidth - margins));
2032 var contentX = (1 - progress) * (floatingFlickable.contentWidth - floatingFlickable.width)
2033 floatingFlickable.contentX = Math.max(0, Math.min(floatingFlickable.contentX, contentX))
2035 if (mouseX > floatingFlickable.width - scrollAreaWidth && progressiveScrollingEnabled) {
2036 var progress = Math.min(1, (mouseX - (floatingFlickable.width - scrollAreaWidth)) / (scrollAreaWidth - margins))
2037 var contentX = progress * (floatingFlickable.contentWidth - floatingFlickable.width)
2038 floatingFlickable.contentX = Math.min(floatingFlickable.contentWidth - floatingFlickable.width, Math.max(floatingFlickable.contentX, contentX))
2044 mouse.accepted = false;
2045 wasTouchPress = mouse.source === Qt.MouseEventSynthesizedByQt;
2048 onExited: wasTouchPress = false;
2052 id: floatingFlickable
2053 objectName: "spreadFlickable"
2054 anchors.fill: appContainer
2056 contentWidth: spreadItem.spreadTotalWidth
2058 function snap(toIndex) {
2059 var delegate = appRepeater.itemAt(toIndex)
2060 var targetContentX = floatingFlickable.contentWidth / spreadItem.totalItemCount * toIndex;
2061 if (targetContentX - floatingFlickable.contentX > spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) {
2062 var offset = (spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) - (targetContentX - floatingFlickable.contentX)
2063 snapAnimation.to = Math.max(0, floatingFlickable.contentX - offset);
2064 snapAnimation.start();
2065 } else if (targetContentX - floatingFlickable.contentX < spreadItem.leftStackXPos + units.gu(1)) {
2066 var offset = (spreadItem.leftStackXPos + units.gu(1)) - (targetContentX - floatingFlickable.contentX);
2067 snapAnimation.to = Math.max(0, floatingFlickable.contentX - offset);
2068 snapAnimation.start();
2071 UbuntuNumberAnimation {id: snapAnimation; target: floatingFlickable; property: "contentX"}
2075 id: shortRightEdgeSwipeAnimation
2078 duration: priv.animationDuration
2082 id: rightEdgeDragArea
2083 objectName: "rightEdgeDragArea"
2084 direction: Direction.Leftwards
2085 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
2086 width: root.dragAreaWidth
2087 enabled: root.spreadEnabled
2089 property var gesturePoints: []
2090 property bool cancelled: false
2092 property real progress: -touchPosition.x / root.width
2093 onProgressChanged: {
2095 draggedProgress = progress;
2099 property real draggedProgress: 0
2101 onTouchPositionChanged: {
2102 gesturePoints.push(touchPosition.x);
2103 if (gesturePoints.length > 10) {
2104 gesturePoints.splice(0, gesturePoints.length - 10)
2108 onDraggingChanged: {
2110 // A potential edge-drag gesture has started. Start recording it
2113 draggedProgress = 0;
2115 // Ok. The user released. Did he drag far enough to go to full spread?
2116 if (gesturePoints[gesturePoints.length - 1] < -spreadItem.rightEdgeBreakPoint * spreadItem.width ) {
2118 // He dragged far enough, but if the last movement was a flick to the right again, he wants to cancel the spread again.
2119 var oneWayFlickToRight = true;
2120 var smallestX = gesturePoints[0]-1;
2121 for (var i = 0; i < gesturePoints.length; i++) {
2122 if (gesturePoints[i] <= smallestX) {
2123 oneWayFlickToRight = false;
2126 smallestX = gesturePoints[i];
2129 if (!oneWayFlickToRight) {
2130 // Ok, the user made it, let's go to spread!
2131 priv.goneToSpread = true;
2136 // Ok, the user didn't drag far enough to cross the breakPoint
2137 // Find out if it was a one-way movement to the left, in which case we just switch directly to next app.
2138 var oneWayFlick = true;
2139 var smallestX = rightEdgeDragArea.width;
2140 for (var i = 0; i < gesturePoints.length; i++) {
2141 if (gesturePoints[i] >= smallestX) {
2142 oneWayFlick = false;
2145 smallestX = gesturePoints[i];
2148 if (appRepeater.count > 1 &&
2149 (oneWayFlick && rightEdgeDragArea.distance > units.gu(2) || rightEdgeDragArea.distance > spreadItem.rightEdgeBreakPoint * spreadItem.width)) {
2150 var nextStage = appRepeater.itemAt(priv.nextInStack).stage
2151 for (var i = 0; i < appRepeater.count; i++) {
2152 if (i != priv.nextInStack && appRepeater.itemAt(i).stage == nextStage) {
2153 appRepeater.itemAt(i).playHidingAnimation()
2157 appRepeater.itemAt(priv.nextInStack).playFocusAnimation()
2158 if (appRepeater.itemAt(priv.nextInStack).stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
2172 TabletSideStageTouchGesture {
2174 objectName: "triGestureArea"
2175 anchors.fill: parent
2177 property Item appDelegate
2179 dragComponent: dragComponent
2180 dragComponentProperties: { "appDelegate": appDelegate }
2183 function matchDelegate(obj) { return String(obj.objectName).indexOf("appDelegate") >= 0; }
2185 var delegateAtCenter = Functions.itemAt(appContainer, x, y, matchDelegate);
2186 if (!delegateAtCenter) return;
2188 appDelegate = delegateAtCenter;
2192 if (sideStage.shown) {
2196 priv.updateMainAndSideStageIndexes()
2201 // If we're dragging to the sidestage.
2202 if (!sideStage.shown) {
2210 property Item appDelegate
2212 surface: appDelegate ? appDelegate.surface : null
2214 consumesInput: false
2217 requestedWidth: appDelegate ? appDelegate.requestedWidth : 0
2218 requestedHeight: appDelegate ? appDelegate.requestedHeight : 0
2221 height: units.gu(40)
2223 Drag.hotSpot.x: width/2
2224 Drag.hotSpot.y: height/2
2225 // only accept opposite stage.
2227 if (!surface) return "Disabled";
2229 if (appDelegate.stage === ApplicationInfo.MainStage) {
2230 if (appDelegate.application.supportedOrientations
2231 & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {