2 * Copyright (C) 2014-2016 Canonical, Ltd.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 import Ubuntu.Components 1.3
19 import Unity.Application 0.1
20 import "../Components/PanelState"
21 import "../Components"
23 import Ubuntu.Gestures 0.1
24 import GlobalShortcut 1.0
27 import "Spread/MathUtils.js" as MathUtils
33 property QtObject applicationManager
34 property QtObject topLevelSurfaceList
35 property bool altTabPressed
36 property url background
37 property int dragAreaWidth
38 property bool interactive
39 property real nativeHeight
40 property real nativeWidth
41 property QtObject orientations
42 property int shellOrientation
43 property int shellOrientationAngle
44 property bool spreadEnabled: true // If false, animations and right edge will be disabled
45 property bool suspended
46 property int leftMargin: 0
47 property bool oskEnabled: false
48 property rect inputMethodRect
51 property string mode: "staged"
53 // Used by the tutorial code
54 readonly property real rightEdgeDragProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0 // How far left the stage has been dragged
56 // used by the snap windows (edge maximize) feature
57 readonly property alias previewRectangle: fakeRectangle
59 readonly property bool spreadShown: state == "spread"
60 readonly property var mainApp: priv.focusedAppDelegate ? priv.focusedAppDelegate.application : null
62 // application windows never rotate independently
63 property int mainAppWindowOrientationAngle: shellOrientationAngle
65 property bool orientationChangesEnabled: !priv.focusedAppDelegate || priv.focusedAppDelegate.orientationChangesEnabled
67 property int supportedOrientations: {
71 return mainApp.supportedOrientations;
72 case "stagedWithSideStage":
73 var orientations = mainApp.supportedOrientations;
74 orientations |= Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation;
75 if (priv.sideStageItemId) {
76 // If we have a sidestage app, support Portrait orientation
77 // so that it will switch the sidestage app to mainstage on rotate to portrait
78 orientations |= Qt.PortraitOrientation|Qt.InvertedPortraitOrientation;
84 return Qt.PortraitOrientation |
85 Qt.LandscapeOrientation |
86 Qt.InvertedPortraitOrientation |
87 Qt.InvertedLandscapeOrientation;
91 onAltTabPressedChanged: {
93 if (root.spreadEnabled) {
94 altTabDelayTimer.start();
97 // Alt Tab has been released, did we already go to spread?
98 if (priv.goneToSpread) {
99 priv.goneToSpread = false;
101 // No we didn't, do a quick alt-tab
102 if (appRepeater.count > 1) {
103 appRepeater.itemAt(1).claimFocus();
114 if (root.altTabPressed) {
115 priv.goneToSpread = true;
120 property Item itemConfiningMouseCursor: !spreadShown && priv.focusedAppDelegate && priv.focusedAppDelegate.window.confinesMousePointer ?
121 priv.focusedAppDelegate.clientAreaItem : null;
123 signal itemSnapshotRequested(Item item)
125 // functions to be called from outside
126 function updateFocusedAppOrientation() { /* TODO */ }
127 function updateFocusedAppOrientationAnimated() { /* TODO */}
128 function pushRightEdge(amount) {
129 if (root.spreadEnabled) {
130 edgeBarrier.push(amount);
134 function closeSpread() {
135 priv.goneToSpread = false;
138 onSpreadEnabledChanged: {
139 if (!spreadEnabled && spreadShown) {
145 id: lifecycleExceptions
146 schema.id: "com.canonical.qtmir"
149 function isExemptFromLifecycle(appId) {
150 var shortAppId = appId.split('_')[0];
151 for (var i = 0; i < lifecycleExceptions.lifecycleExemptAppids.length; i++) {
152 if (shortAppId === lifecycleExceptions.lifecycleExemptAppids[i]) {
160 id: closeFocusedShortcut
161 shortcut: Qt.AltModifier|Qt.Key_F4
163 if (priv.focusedAppDelegate && !priv.focusedAppDelegate.isDash) {
164 priv.focusedAppDelegate.close();
170 id: showSpreadShortcut
171 shortcut: Qt.MetaModifier|Qt.Key_W
172 active: root.spreadEnabled
173 onTriggered: priv.goneToSpread = true
177 id: minimizeAllShortcut
178 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_D
179 onTriggered: priv.minimizeAllWindows()
180 active: root.state == "windowed"
184 id: maximizeWindowShortcut
185 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up
186 onTriggered: priv.focusedAppDelegate.requestMaximize()
187 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximized
191 id: maximizeWindowLeftShortcut
192 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left
193 onTriggered: priv.focusedAppDelegate.requestMaximizeLeft()
194 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight
198 id: maximizeWindowRightShortcut
199 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right
200 onTriggered: priv.focusedAppDelegate.requestMaximizeRight()
201 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight
205 id: minimizeRestoreShortcut
206 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
208 if (priv.focusedAppDelegate.anyMaximized) {
209 priv.focusedAppDelegate.requestRestore();
211 priv.focusedAppDelegate.requestMinimize();
214 active: root.state == "windowed" && priv.focusedAppDelegate
218 shortcut: Qt.AltModifier|Qt.Key_Print
219 onTriggered: root.itemSnapshotRequested(priv.focusedAppDelegate)
220 active: priv.focusedAppDelegate !== null
225 objectName: "DesktopStagePrivate"
227 property var focusedAppDelegate: null
228 property var foregroundMaximizedAppDelegate: null // for stuff like drop shadow and focusing maximized app by clicking panel
230 property bool goneToSpread: false
231 property int closingIndex: -1
232 property int animationDuration: UbuntuAnimation.FastDuration
234 function updateForegroundMaximizedApp() {
236 for (var i = 0; i < appRepeater.count && !found; i++) {
237 var item = appRepeater.itemAt(i);
238 if (item && item.visuallyMaximized) {
239 foregroundMaximizedAppDelegate = item;
244 foregroundMaximizedAppDelegate = null;
248 function minimizeAllWindows() {
249 for (var i = appRepeater.count - 1; i >= 0; i--) {
250 var appDelegate = appRepeater.itemAt(i);
251 if (appDelegate && !appDelegate.minimized) {
252 appDelegate.requestMinimize();
257 readonly property bool sideStageEnabled: root.mode === "stagedWithSideStage" &&
258 (root.shellOrientation == Qt.LandscapeOrientation ||
259 root.shellOrientation == Qt.InvertedLandscapeOrientation)
260 onSideStageEnabledChanged: {
261 for (var i = 0; i < appRepeater.count; i++) {
262 appRepeater.itemAt(i).refreshStage();
264 priv.updateMainAndSideStageIndexes();
267 property var mainStageDelegate: null
268 property var sideStageDelegate: null
269 property int mainStageItemId: 0
270 property int sideStageItemId: 0
271 property string mainStageAppId: ""
272 property string sideStageAppId: ""
274 onSideStageDelegateChanged: {
275 if (!sideStageDelegate) {
280 function updateMainAndSideStageIndexes() {
281 if (root.mode != "stagedWithSideStage") {
282 priv.sideStageDelegate = null;
283 priv.sideStageItemId = 0;
284 priv.sideStageAppId = "";
285 priv.mainStageDelegate = appRepeater.itemAt(0);
286 priv.mainStageItemId = topLevelSurfaceList.idAt(0);
287 priv.mainStageAppId = topLevelSurfaceList.applicationAt(0) ? topLevelSurfaceList.applicationAt(0).appId : ""
291 var choseMainStage = false;
292 var choseSideStage = false;
294 if (!root.topLevelSurfaceList)
297 for (var i = 0; i < appRepeater.count && (!choseMainStage || !choseSideStage); ++i) {
298 var appDelegate = appRepeater.itemAt(i);
300 // This might happen during startup phase... If the delegate appears and claims focus
301 // things are updated and appRepeater.itemAt(x) still returns null while appRepeater.count >= x
302 // Lets just skip it, on startup it will be generated at a later point too...
305 if (sideStage.shown && appDelegate.stage == ApplicationInfoInterface.SideStage
306 && !choseSideStage) {
307 priv.sideStageDelegate = appDelegate
308 priv.sideStageItemId = root.topLevelSurfaceList.idAt(i);
309 priv.sideStageAppId = root.topLevelSurfaceList.applicationAt(i).appId;
310 choseSideStage = true;
311 } else if (!choseMainStage && appDelegate.stage == ApplicationInfoInterface.MainStage) {
312 priv.mainStageDelegate = appDelegate;
313 priv.mainStageItemId = root.topLevelSurfaceList.idAt(i);
314 priv.mainStageAppId = root.topLevelSurfaceList.applicationAt(i).appId;
315 choseMainStage = true;
318 if (!choseMainStage && priv.mainStageDelegate) {
319 priv.mainStageDelegate = null;
320 priv.mainStageItemId = 0;
321 priv.mainStageAppId = "";
323 if (!choseSideStage && priv.sideStageDelegate) {
324 priv.sideStageDelegate = null;
325 priv.sideStageItemId = 0;
326 priv.sideStageAppId = "";
330 property int nextInStack: {
331 var mainStageIndex = priv.mainStageDelegate ? priv.mainStageDelegate.itemIndex : -1;
332 var sideStageIndex = priv.sideStageDelegate ? priv.sideStageDelegate.itemIndex : -1;
333 if (sideStageIndex == -1) {
334 return topLevelSurfaceList.count > 1 ? 1 : -1;
336 if (mainStageIndex == 0 || sideStageIndex == 0) {
337 if (mainStageIndex == 1 || sideStageIndex == 1) {
338 return topLevelSurfaceList.count > 2 ? 2 : -1;
345 readonly property real virtualKeyboardHeight: root.inputMethodRect.height
348 Component.onCompleted: priv.updateMainAndSideStageIndexes();
352 onCloseClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
353 onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestMinimize(); } }
354 onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestRestore(); } }
359 property: "buttonsVisible"
360 value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized // FIXME for Locally integrated menus
367 if (priv.focusedAppDelegate !== null) {
368 if (priv.focusedAppDelegate.maximized)
369 return priv.focusedAppDelegate.title
371 return priv.focusedAppDelegate.appName
375 when: priv.focusedAppDelegate
380 property: "dropShadow"
381 value: priv.focusedAppDelegate && !priv.focusedAppDelegate.maximized && priv.foregroundMaximizedAppDelegate !== null && mode == "windowed"
386 property: "closeButtonShown"
387 value: priv.focusedAppDelegate && priv.focusedAppDelegate.maximized && !priv.focusedAppDelegate.isDash
390 Component.onDestruction: {
391 PanelState.title = "";
392 PanelState.buttonsVisible = false;
393 PanelState.dropShadow = false;
397 model: root.applicationManager
399 property var stateBinding: Binding {
400 readonly property bool isDash: model.application ? model.application.appId == "unity8-dash" : false
401 target: model.application
402 property: "requestedState"
404 // TODO: figure out some lifecycle policy, like suspending minimized apps
405 // or something if running windowed.
406 // TODO: If the device has a dozen suspended apps because it was running
407 // in staged mode, when it switches to Windowed mode it will suddenly
408 // resume all those apps at once. We might want to avoid that.
409 value: root.mode === "windowed"
411 || (!root.suspended && model.application && priv.focusedAppDelegate &&
412 (priv.focusedAppDelegate.appId === model.application.appId ||
413 priv.mainStageAppId === model.application.appId ||
414 priv.sideStageAppId === model.application.appId))
415 ? ApplicationInfoInterface.RequestedRunning
416 : ApplicationInfoInterface.RequestedSuspended
419 property var lifecycleBinding: Binding {
420 target: model.application
421 property: "exemptFromLifecycle"
422 value: model.application
423 ? (!model.application.isTouchApp || isExemptFromLifecycle(model.application.appId))
431 name: "spread"; when: priv.goneToSpread
432 PropertyChanges { target: floatingFlickable; enabled: true }
433 PropertyChanges { target: spreadItem; focus: true }
434 PropertyChanges { target: hoverMouseArea; enabled: true }
435 PropertyChanges { target: rightEdgeDragArea; enabled: false }
436 PropertyChanges { target: cancelSpreadMouseArea; enabled: true }
437 PropertyChanges { target: blurLayer; visible: true; blurRadius: 32; brightness: .65; opacity: 1 }
438 PropertyChanges { target: wallpaper; visible: false }
441 name: "stagedRightEdge"; when: (rightEdgeDragArea.dragging || edgeBarrier.progress > 0) && root.mode == "staged"
451 name: "sideStagedRightEdge"; when: (rightEdgeDragArea.dragging || edgeBarrier.progress > 0) && root.mode == "stagedWithSideStage"
452 extend: "stagedRightEdge"
455 opacity: priv.sideStageDelegate.x === sideStage.x ? 1 : 0
460 name: "windowedRightEdge"; when: (rightEdgeDragArea.dragging || edgeBarrier.progress > 0) && root.mode == "windowed"
466 opacity: MathUtils.linearAnimation(spreadItem.rightEdgeBreakPoint, 1, 0, 1, Math.max(rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0, edgeBarrier.progress))
470 name: "staged"; when: root.mode === "staged"
471 PropertyChanges { target: wallpaper; visible: false }
474 name: "stagedWithSideStage"; when: root.mode === "stagedWithSideStage"
475 PropertyChanges { target: triGestureArea; enabled: priv.sideStageEnabled }
476 PropertyChanges { target: sideStage; visible: true }
479 name: "windowed"; when: root.mode === "windowed"
484 from: "stagedRightEdge,sideStagedRightEdge,windowedRightEdge"; to: "spread"
485 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
486 PropertyAnimation { target: blurLayer; properties: "brightness,blurRadius"; duration: priv.animationDuration }
490 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: appRepeater.count > 1 ? 1 : 0 }
491 PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
495 SequentialAnimation {
498 var item = appRepeater.itemAt(Math.max(0, spreadItem.highlightedIndex));
499 if (item.stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
502 item.playFocusAnimation();
505 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
509 to: "stagedRightEdge,sideStagedRightEdge"
510 PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
513 to: "stagedWithSideStage"
514 ScriptAction { script: priv.updateMainAndSideStageIndexes(); }
520 id: cancelSpreadMouseArea
523 onClicked: priv.goneToSpread = false
528 objectName: "appContainer"
535 source: root.background
536 // Make sure it's the lowest item. Due to the left edge drag we sometimes need
537 // to put the dash at -1 and we don't want it behind the Wallpaper
550 objectName: "spreadItem"
551 anchors.fill: appContainer
552 leftMargin: root.leftMargin
553 model: root.topLevelSurfaceList
554 spreadFlickable: floatingFlickable
558 priv.goneToSpread = false;
563 target: root.topLevelSurfaceList
564 onListChanged: priv.updateMainAndSideStageIndexes()
569 objectName: "MainStageDropArea"
573 bottom: parent.bottom
575 width: appContainer.width - sideStage.width
576 enabled: priv.sideStageEnabled
579 drop.source.appDelegate.saveStage(ApplicationInfoInterface.MainStage);
580 drop.source.appDelegate.focus = true;
587 objectName: "sideStage"
589 height: appContainer.height
590 x: appContainer.width - width
592 Behavior on opacity { UbuntuNumberAnimation {} }
594 if (!priv.mainStageItemId) return 0;
596 if (priv.sideStageItemId && priv.nextInStack > 0) {
598 // Due the order in which bindings are evaluated, this might be triggered while shuffling
599 // the list and index doesn't yet match with itemIndex (even though itemIndex: index)
600 // Let's walk the list and compare itemIndex to make sure we have the correct one.
601 var nextDelegateInStack = -1;
602 for (var i = 0; i < appRepeater.count; i++) {
603 if (appRepeater.itemAt(i).itemIndex == priv.nextInStack) {
604 nextDelegateInStack = appRepeater.itemAt(i);
609 if (nextDelegateInStack.stage === ApplicationInfoInterface.MainStage) {
610 // if the next app in stack is a main stage app, put the sidestage on top of it.
620 if (!shown && priv.mainStageDelegate && !root.spreadShown) {
621 priv.mainStageDelegate.activate();
626 id: sideStageDropArea
627 objectName: "SideStageDropArea"
630 property bool dropAllowed: true
633 dropAllowed = drag.keys != "Disabled";
639 if (drop.keys == "MainStage") {
640 drop.source.appDelegate.saveStage(ApplicationInfoInterface.SideStage);
641 drop.source.appDelegate.focus = true;
646 if (!sideStageDropArea.drag.source) {
656 model: topLevelSurfaceList
657 objectName: "appRepeater"
659 function indexOf(delegateItem) {
660 for (var i = 0; i < count; i++) {
661 if (itemAt(i) === delegateItem) {
668 delegate: FocusScope {
670 objectName: "appDelegate_" + model.window.id
671 property int itemIndex: index // We need this from outside the repeater
672 // z might be overriden in some cases by effects, but we need z ordering
673 // to calculate occlusion detection
674 property int normalZ: topLevelSurfaceList.count - index
676 if (visuallyMaximized) {
677 priv.updateForegroundMaximizedApp();
682 // Normally we want x/y where the surface thinks it is. Width/height of our delegate will
683 // match what the actual surface size is.
684 // Don't write to those, they will be set by states
685 x: model.window.position.x - clientAreaItem.x
686 y: model.window.position.y - clientAreaItem.y
687 width: decoratedWindow.implicitWidth
688 height: decoratedWindow.implicitHeight
690 // requestedX/Y/width/height is what we ask the actual surface to be.
691 // Do not write to those, they will be set by states
692 property real requestedX: windowedX
693 property real requestedY: windowedY
694 property real requestedWidth: windowedWidth
695 property real requestedHeight: windowedHeight
697 target: model.window; property: "requestedPosition"
698 // miral doesn't know about our window decorations. So we have to deduct them
699 value: Qt.point(appDelegate.requestedX + appDelegate.clientAreaItem.x,
700 appDelegate.requestedY + appDelegate.clientAreaItem.y)
703 // In those are for windowed mode. Those values basically store the window's properties
704 // when having a floating window. If you want to move/resize a window in normal mode, this is what you want to write to.
705 property real windowedX
706 property real windowedY
707 property real windowedWidth
708 property real windowedHeight
710 // unlike windowedX/Y, this is the last known grab position before being pushed against edges/corners
711 // when restoring, the window should return to these, not to the place where it was dropped near the edge
712 property real restoredX
713 property real restoredY
715 // Keeps track of the window geometry while in normal or restored state
716 // Useful when returning from some maxmized state or when saving the geometry while maximized
717 // FIXME: find a better solution
718 property real normalX: 0
719 property real normalY: 0
720 property real normalWidth: 0
721 property real normalHeight: 0
722 function updateNormalGeometry() {
723 if (appDelegate.state == "normal" || appDelegate.state == "restored") {
724 normalX = appDelegate.requestedX;
725 normalY = appDelegate.requestedY;
726 normalWidth = appDelegate.width;
727 normalHeight = appDelegate.height;
732 onXChanged: appDelegate.updateNormalGeometry();
733 onYChanged: appDelegate.updateNormalGeometry();
734 onWidthChanged: appDelegate.updateNormalGeometry();
735 onHeightChanged: appDelegate.updateNormalGeometry();
741 value: appDelegate.requestedY -
742 Math.min(appDelegate.requestedY - PanelState.panelHeight,
743 Math.max(0, priv.virtualKeyboardHeight - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
744 when: root.oskEnabled && appDelegate.focus && (appDelegate.state == "normal" || appDelegate.state == "restored")
745 && root.inputMethodRect.height > 0
749 Behavior on x { id: xBehavior; enabled: priv.closingIndex >= 0; UbuntuNumberAnimation { onRunningChanged: if (!running) priv.closingIndex = -1} }
753 onShellOrientationAngleChanged: {
754 // at this point decoratedWindow.surfaceOrientationAngle is the old shellOrientationAngle
755 if (application && application.rotatesWindowContents) {
756 if (root.state == "windowed") {
757 var angleDiff = decoratedWindow.surfaceOrientationAngle - shellOrientationAngle;
758 angleDiff = (360 + angleDiff) % 360;
759 if (angleDiff === 90 || angleDiff === 270) {
760 var aux = decoratedWindow.requestedHeight;
761 decoratedWindow.requestedHeight = decoratedWindow.requestedWidth + decoratedWindow.decorationHeight;
762 decoratedWindow.requestedWidth = aux - decoratedWindow.decorationHeight;
765 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
767 decoratedWindow.surfaceOrientationAngle = 0;
772 readonly property alias application: decoratedWindow.application
773 readonly property alias minimumWidth: decoratedWindow.minimumWidth
774 readonly property alias minimumHeight: decoratedWindow.minimumHeight
775 readonly property alias maximumWidth: decoratedWindow.maximumWidth
776 readonly property alias maximumHeight: decoratedWindow.maximumHeight
777 readonly property alias widthIncrement: decoratedWindow.widthIncrement
778 readonly property alias heightIncrement: decoratedWindow.heightIncrement
780 readonly property bool maximized: windowState === WindowStateStorage.WindowStateMaximized
781 readonly property bool maximizedLeft: windowState === WindowStateStorage.WindowStateMaximizedLeft
782 readonly property bool maximizedRight: windowState === WindowStateStorage.WindowStateMaximizedRight
783 readonly property bool maximizedHorizontally: windowState === WindowStateStorage.WindowStateMaximizedHorizontally
784 readonly property bool maximizedVertically: windowState === WindowStateStorage.WindowStateMaximizedVertically
785 readonly property bool maximizedTopLeft: windowState === WindowStateStorage.WindowStateMaximizedTopLeft
786 readonly property bool maximizedTopRight: windowState === WindowStateStorage.WindowStateMaximizedTopRight
787 readonly property bool maximizedBottomLeft: windowState === WindowStateStorage.WindowStateMaximizedBottomLeft
788 readonly property bool maximizedBottomRight: windowState === WindowStateStorage.WindowStateMaximizedBottomRight
789 readonly property bool anyMaximized: maximized || maximizedLeft || maximizedRight || maximizedHorizontally || maximizedVertically ||
790 maximizedTopLeft || maximizedTopRight || maximizedBottomLeft || maximizedBottomRight
792 readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
793 readonly property bool fullscreen: window.state === Mir.FullscreenState
795 readonly property bool canBeMaximized: canBeMaximizedHorizontally && canBeMaximizedVertically
796 readonly property bool canBeMaximizedLeftRight: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
797 (maximumHeight == 0 || maximumHeight >= appContainer.height)
798 readonly property bool canBeCornerMaximized: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
799 (maximumHeight == 0 || maximumHeight >= appContainer.height/2)
800 readonly property bool canBeMaximizedHorizontally: maximumWidth == 0 || maximumWidth >= appContainer.width
801 readonly property bool canBeMaximizedVertically: maximumHeight == 0 || maximumHeight >= appContainer.height
802 readonly property alias orientationChangesEnabled: decoratedWindow.orientationChangesEnabled
804 property int windowState: WindowStateStorage.WindowStateNormal
805 property bool animationsEnabled: true
806 property alias title: decoratedWindow.title
807 readonly property string appName: model.application ? model.application.name : ""
808 property bool visuallyMaximized: false
809 property bool visuallyMinimized: false
810 readonly property alias windowedTransitionRunning: windowedTransition.running
812 property int stage: ApplicationInfoInterface.MainStage
813 function saveStage(newStage) {
814 appDelegate.stage = newStage;
815 WindowStateStorage.saveStage(appId, newStage);
816 priv.updateMainAndSideStageIndexes()
819 readonly property var surface: model.window.surface
820 readonly property var window: model.window
822 readonly property alias resizeArea: resizeArea
823 readonly property alias focusedSurface: decoratedWindow.focusedSurface
824 readonly property bool dragging: touchControls.overlayShown ? touchControls.dragging : decoratedWindow.dragging
826 readonly property string appId: model.application.appId
827 readonly property bool isDash: appId == "unity8-dash"
828 readonly property alias clientAreaItem: decoratedWindow.clientAreaItem
830 function activate() {
831 if (model.window.focused) {
832 updateQmlFocusFromMirSurfaceFocus();
834 model.window.activate();
837 function requestMaximize() { model.window.requestState(Mir.MaximizedState); }
838 function requestMaximizeVertically() { model.window.requestState(Mir.VertMaximizedState); }
839 function requestMaximizeHorizontally() { model.window.requestState(Mir.HorizMaximizedState); }
840 function requestMaximizeLeft() { model.window.requestState(Mir.MaximizedLeftState); }
841 function requestMaximizeRight() { model.window.requestState(Mir.MaximizedRightState); }
842 function requestMaximizeTopLeft() { model.window.requestState(Mir.MaximizedTopLeftState); }
843 function requestMaximizeTopRight() { model.window.requestState(Mir.MaximizedTopRightState); }
844 function requestMaximizeBottomLeft() { model.window.requestState(Mir.MaximizedBottomLeftState); }
845 function requestMaximizeBottomRight() { model.window.requestState(Mir.MaximizedBottomRightState); }
846 function requestMinimize() { model.window.requestState(Mir.MinimizedState); }
847 function requestRestore() { model.window.requestState(Mir.RestoredState); }
849 function claimFocus() {
850 if (root.state == "spread") {
851 spreadItem.highlightedIndex = index
852 priv.goneToSpread = false;
854 if (root.mode == "stagedWithSideStage") {
855 if (appDelegate.stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
858 priv.updateMainAndSideStageIndexes();
860 if (root.mode == "windowed") {
861 appDelegate.restore(true /* animated */, appDelegate.windowState);
863 appDelegate.focus = true;
866 function updateQmlFocusFromMirSurfaceFocus() {
867 if (model.window.focused) {
869 priv.focusedAppDelegate = appDelegate;
876 screenWidth: appContainer.width
877 screenHeight: appContainer.height
878 leftMargin: root.leftMargin
879 minimumY: PanelState.panelHeight
885 updateQmlFocusFromMirSurfaceFocus();
888 appDelegate.activate();
891 if (model.window.state === Mir.MinimizedState) {
892 appDelegate.minimize();
893 } else if (model.window.state === Mir.MaximizedState) {
894 appDelegate.maximize();
895 } else if (model.window.state === Mir.VertMaximizedState) {
896 appDelegate.maximizeVertically();
897 } else if (model.window.state === Mir.HorizMaximizedState) {
898 appDelegate.maximizeHorizontally();
899 } else if (model.window.state === Mir.MaximizedLeftState) {
900 appDelegate.maximizeLeft();
901 } else if (model.window.state === Mir.MaximizedRightState) {
902 appDelegate.maximizeRight();
903 } else if (model.window.state === Mir.MaximizedTopLeftState) {
904 appDelegate.maximizeTopLeft();
905 } else if (model.window.state === Mir.MaximizedTopRightState) {
906 appDelegate.maximizeTopRight();
907 } else if (model.window.state === Mir.MaximizedBottomLeftState) {
908 appDelegate.maximizeBottomLeft();
909 } else if (model.window.state === Mir.MaximizedBottomRightState) {
910 appDelegate.maximizeBottomRight();
911 } else if (model.window.state === Mir.RestoredState) {
912 appDelegate.restore();
917 Component.onCompleted: {
918 if (application && application.rotatesWindowContents) {
919 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
921 decoratedWindow.surfaceOrientationAngle = 0;
924 // First, cascade the newly created window, relative to the currently/old focused window.
925 windowedX = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedX + units.gu(3) : (normalZ - 1) * units.gu(3)
926 windowedY = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedY + units.gu(3) : normalZ * units.gu(3)
927 // Now load any saved state. This needs to happen *after* the cascading!
928 windowStateSaver.load();
929 model.window.requestState(WindowStateStorage.toMirState(windowState));
931 updateQmlFocusFromMirSurfaceFocus();
934 _constructing = false;
936 Component.onDestruction: {
937 windowStateSaver.save();
940 // This stage is about to be destroyed. Don't mess up with the model at this point
944 if (visuallyMaximized) {
945 priv.updateForegroundMaximizedApp();
949 onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
951 property bool _constructing: true;
953 if (!_constructing) {
954 priv.updateMainAndSideStageIndexes();
960 && !greeter.fullyShown
961 && (priv.foregroundMaximizedAppDelegate === null || priv.foregroundMaximizedAppDelegate.normalZ <= z)
963 || appDelegate.fullscreen
964 || focusAnimation.running || rightEdgeFocusAnimation.running || hidingAnimation.running
967 model.window.close();
970 function maximize(animated) {
971 animationsEnabled = (animated === undefined) || animated;
972 windowState = WindowStateStorage.WindowStateMaximized;
974 function maximizeLeft(animated) {
975 animationsEnabled = (animated === undefined) || animated;
976 windowState = WindowStateStorage.WindowStateMaximizedLeft;
978 function maximizeRight(animated) {
979 animationsEnabled = (animated === undefined) || animated;
980 windowState = WindowStateStorage.WindowStateMaximizedRight;
982 function maximizeHorizontally(animated) {
983 animationsEnabled = (animated === undefined) || animated;
984 windowState = WindowStateStorage.WindowStateMaximizedHorizontally;
986 function maximizeVertically(animated) {
987 animationsEnabled = (animated === undefined) || animated;
988 windowState = WindowStateStorage.WindowStateMaximizedVertically;
990 function maximizeTopLeft(animated) {
991 animationsEnabled = (animated === undefined) || animated;
992 windowState = WindowStateStorage.WindowStateMaximizedTopLeft;
994 function maximizeTopRight(animated) {
995 animationsEnabled = (animated === undefined) || animated;
996 windowState = WindowStateStorage.WindowStateMaximizedTopRight;
998 function maximizeBottomLeft(animated) {
999 animationsEnabled = (animated === undefined) || animated;
1000 windowState = WindowStateStorage.WindowStateMaximizedBottomLeft;
1002 function maximizeBottomRight(animated) {
1003 animationsEnabled = (animated === undefined) || animated;
1004 windowState = WindowStateStorage.WindowStateMaximizedBottomRight;
1006 function minimize(animated) {
1007 animationsEnabled = (animated === undefined) || animated;
1008 windowState |= WindowStateStorage.WindowStateMinimized; // add the minimized bit
1010 function restore(animated,state) {
1011 animationsEnabled = (animated === undefined) || animated;
1012 windowState = state || WindowStateStorage.WindowStateRestored;
1013 windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
1016 function playFocusAnimation() {
1017 if (state == "stagedRightEdge") {
1018 // TODO: Can we drop this if and find something that always works?
1019 if (root.mode == "staged") {
1020 rightEdgeFocusAnimation.targetX = 0
1021 rightEdgeFocusAnimation.start()
1022 } else if (root.mode == "stagedWithSideStage") {
1023 rightEdgeFocusAnimation.targetX = appDelegate.stage == ApplicationInfoInterface.SideStage ? sideStage.x : 0
1024 rightEdgeFocusAnimation.start()
1026 } else if (state == "windowedRightEdge" || state == "windowed") {
1029 focusAnimation.start()
1032 function playHidingAnimation() {
1033 if (state != "windowedRightEdge") {
1034 hidingAnimation.start()
1038 function refreshStage() {
1039 var newStage = ApplicationInfoInterface.MainStage;
1040 if (priv.sideStageEnabled) { // we're in lanscape rotation.
1041 if (!isDash && application && application.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {
1042 var defaultStage = ApplicationInfoInterface.SideStage; // if application supports portrait, it defaults to sidestage.
1043 if (application.supportedOrientations & (Qt.LandscapeOrientation|Qt.InvertedLandscapeOrientation)) {
1044 // if it supports lanscape, it defaults to mainstage.
1045 defaultStage = ApplicationInfoInterface.MainStage;
1047 newStage = WindowStateStorage.getStage(application.appId, defaultStage);
1052 if (focus && stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
1057 UbuntuNumberAnimation {
1063 duration: UbuntuAnimation.SnapDuration
1065 topLevelSurfaceList.raiseId(model.window.id);
1068 appDelegate.activate();
1072 id: rightEdgeFocusAnimation
1073 property int targetX: 0
1074 UbuntuNumberAnimation { target: appDelegate; properties: "x"; to: rightEdgeFocusAnimation.targetX; duration: priv.animationDuration }
1075 UbuntuNumberAnimation { target: decoratedWindow; properties: "angle"; to: 0; duration: priv.animationDuration }
1076 UbuntuNumberAnimation { target: decoratedWindow; properties: "itemScale"; to: 1; duration: priv.animationDuration }
1078 appDelegate.activate();
1083 UbuntuNumberAnimation { target: appDelegate; property: "opacity"; to: 0; duration: priv.animationDuration }
1084 onStopped: appDelegate.opacity = 1
1091 flickable: floatingFlickable
1095 sceneWidth: root.width
1096 stage: appDelegate.stage
1097 thisDelegate: appDelegate
1098 mainStageDelegate: priv.mainStageDelegate
1099 sideStageDelegate: priv.sideStageDelegate
1100 sideStageWidth: sideStage.panelWidth
1101 sideStageX: sideStage.x
1102 itemIndex: appDelegate.itemIndex
1103 nextInStack: priv.nextInStack
1106 StagedRightEdgeMaths {
1107 id: stagedRightEdgeMaths
1108 sceneWidth: appContainer.width - root.leftMargin
1109 sceneHeight: appContainer.height
1110 isMainStageApp: priv.mainStageDelegate == appDelegate
1111 isSideStageApp: priv.sideStageDelegate == appDelegate
1112 sideStageWidth: sideStage.width
1113 sideStageOpen: sideStage.shown
1115 nextInStack: priv.nextInStack
1117 targetHeight: spreadItem.stackHeight
1118 targetX: spreadMaths.targetX
1119 startY: appDelegate.fullscreen ? 0 : PanelState.panelHeight
1120 targetY: spreadMaths.targetY
1121 targetAngle: spreadMaths.targetAngle
1122 targetScale: spreadMaths.targetScale
1123 shuffledZ: stageMaths.itemZ
1124 breakPoint: spreadItem.rightEdgeBreakPoint
1127 WindowedRightEdgeMaths {
1128 id: windowedRightEdgeMaths
1130 startWidth: appDelegate.requestedWidth
1131 startHeight: appDelegate.requestedHeight
1132 targetHeight: spreadItem.stackHeight
1133 targetX: spreadMaths.targetX
1134 targetY: spreadMaths.targetY
1135 normalZ: appDelegate.normalZ
1136 targetAngle: spreadMaths.targetAngle
1137 targetScale: spreadMaths.targetScale
1138 breakPoint: spreadItem.rightEdgeBreakPoint
1143 name: "spread"; when: root.state == "spread"
1145 target: decoratedWindow;
1146 showDecoration: false;
1147 angle: spreadMaths.targetAngle
1148 itemScale: spreadMaths.targetScale
1149 scaleToPreviewSize: spreadItem.stackHeight
1150 scaleToPreviewProgress: 1
1151 hasDecoration: root.mode === "windowed"
1152 shadowOpacity: spreadMaths.shadowOpacity
1153 showHighlight: spreadItem.highlightedIndex === index
1154 darkening: spreadItem.highlightedIndex >= 0
1155 anchors.topMargin: dragArea.distance
1160 x: spreadMaths.targetX
1161 y: spreadMaths.targetY
1163 height: spreadItem.spreadItemHeight
1164 requestedWidth: decoratedWindow.oldRequestedWidth
1165 requestedHeight: decoratedWindow.oldRequestedHeight
1166 visible: spreadMaths.itemVisible
1168 PropertyChanges { target: dragArea; enabled: true }
1169 PropertyChanges { target: windowInfoItem; opacity: spreadMaths.tileInfoOpacity; visible: spreadMaths.itemVisible }
1172 name: "stagedRightEdge"
1173 when: (root.mode == "staged" || root.mode == "stagedWithSideStage") && (root.state == "sideStagedRightEdge" || root.state == "stagedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running)
1175 target: stagedRightEdgeMaths
1176 progress: Math.max(edgeBarrier.progress, rightEdgeDragArea.draggedProgress)
1180 x: stagedRightEdgeMaths.animatedX
1181 y: stagedRightEdgeMaths.animatedY
1182 z: stagedRightEdgeMaths.animatedZ
1183 height: stagedRightEdgeMaths.animatedHeight
1184 requestedWidth: decoratedWindow.oldRequestedWidth
1185 requestedHeight: decoratedWindow.oldRequestedHeight
1186 visible: appDelegate.x < root.width
1189 target: decoratedWindow
1190 hasDecoration: false
1191 angle: stagedRightEdgeMaths.animatedAngle
1192 itemScale: stagedRightEdgeMaths.animatedScale
1193 scaleToPreviewSize: spreadItem.stackHeight
1194 scaleToPreviewProgress: stagedRightEdgeMaths.scaleToPreviewProgress
1198 // make sure it's visible but transparent so it fades in when we transition to spread
1199 PropertyChanges { target: windowInfoItem; opacity: 0; visible: true }
1202 name: "windowedRightEdge"
1203 when: root.mode == "windowed" && (root.state == "windowedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running || edgeBarrier.progress > 0)
1205 target: windowedRightEdgeMaths
1206 swipeProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0
1207 pushProgress: edgeBarrier.progress
1211 x: windowedRightEdgeMaths.animatedX
1212 y: windowedRightEdgeMaths.animatedY
1213 z: windowedRightEdgeMaths.animatedZ
1214 height: stagedRightEdgeMaths.animatedHeight
1215 requestedWidth: decoratedWindow.oldRequestedWidth
1216 requestedHeight: decoratedWindow.oldRequestedHeight
1219 target: decoratedWindow
1220 showDecoration: windowedRightEdgeMaths.decorationHeight
1221 angle: windowedRightEdgeMaths.animatedAngle
1222 itemScale: windowedRightEdgeMaths.animatedScale
1223 scaleToPreviewSize: spreadItem.stackHeight
1224 scaleToPreviewProgress: windowedRightEdgeMaths.scaleToPreviewProgress
1228 target: opacityEffect;
1229 opacityValue: windowedRightEdgeMaths.opacityMask
1230 sourceItem: windowedRightEdgeMaths.opacityMask < 1 ? decoratedWindow : null
1234 name: "staged"; when: root.state == "staged"
1238 y: appDelegate.fullscreen ? 0 : PanelState.panelHeight
1239 requestedWidth: appContainer.width
1240 requestedHeight: appDelegate.fullscreen ? appContainer.height : appContainer.height - PanelState.panelHeight
1241 visuallyMaximized: true
1242 visible: appDelegate.x < root.width
1245 target: decoratedWindow
1246 hasDecoration: false
1254 animateX: !focusAnimation.running && itemIndex !== spreadItem.highlightedIndex
1258 name: "stagedWithSideStage"; when: root.state == "stagedWithSideStage"
1266 y: appDelegate.fullscreen ? 0 : PanelState.panelHeight
1268 requestedWidth: stageMaths.itemWidth
1269 requestedHeight: appDelegate.fullscreen ? appContainer.height : appContainer.height - PanelState.panelHeight
1270 visuallyMaximized: true
1271 visible: appDelegate.x < root.width
1274 target: decoratedWindow
1275 hasDecoration: false
1283 name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
1285 target: appDelegate;
1286 requestedX: root.leftMargin;
1288 visuallyMinimized: false;
1289 visuallyMaximized: true
1290 requestedWidth: appContainer.width - root.leftMargin;
1291 requestedHeight: appContainer.height;
1293 PropertyChanges { target: touchControls; enabled: true }
1296 name: "fullscreen"; when: appDelegate.fullscreen && !appDelegate.minimized
1298 target: appDelegate;
1301 requestedWidth: appContainer.width;
1302 requestedHeight: appContainer.height;
1304 PropertyChanges { target: decoratedWindow; hasDecoration: false }
1308 when: appDelegate.windowState == WindowStateStorage.WindowStateNormal
1311 visuallyMinimized: false
1312 visuallyMaximized: false
1314 PropertyChanges { target: touchControls; enabled: true }
1315 PropertyChanges { target: resizeArea; enabled: true }
1316 PropertyChanges { target: decoratedWindow; shadowOpacity: .3}
1320 when: appDelegate.windowState == WindowStateStorage.WindowStateRestored
1323 target: appDelegate;
1324 windowedX: restoredX;
1325 windowedY: restoredY;
1329 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
1333 windowedX: root.leftMargin
1334 windowedY: PanelState.panelHeight
1335 windowedWidth: (appContainer.width - root.leftMargin)/2
1336 windowedHeight: appContainer.height - PanelState.panelHeight
1340 name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
1341 extend: "maximizedLeft"
1343 target: appDelegate;
1344 windowedX: (appContainer.width + root.leftMargin)/2
1348 name: "maximizedTopLeft"; when: appDelegate.maximizedTopLeft && !appDelegate.minimized
1352 windowedX: root.leftMargin
1353 windowedY: PanelState.panelHeight
1354 windowedWidth: (appContainer.width - root.leftMargin)/2
1355 windowedHeight: (appContainer.height - PanelState.panelHeight)/2
1359 name: "maximizedTopRight"; when: appDelegate.maximizedTopRight && !appDelegate.minimized
1360 extend: "maximizedTopLeft"
1363 windowedX: (appContainer.width + root.leftMargin)/2
1367 name: "maximizedBottomLeft"; when: appDelegate.maximizedBottomLeft && !appDelegate.minimized
1371 windowedX: root.leftMargin
1372 windowedY: (appContainer.height + PanelState.panelHeight)/2
1373 windowedWidth: (appContainer.width - root.leftMargin)/2
1374 windowedHeight: appContainer.height/2
1378 name: "maximizedBottomRight"; when: appDelegate.maximizedBottomRight && !appDelegate.minimized
1379 extend: "maximizedBottomLeft"
1382 windowedX: (appContainer.width + root.leftMargin)/2
1386 name: "maximizedHorizontally"; when: appDelegate.maximizedHorizontally && !appDelegate.minimized
1388 PropertyChanges { target: appDelegate; windowedX: root.leftMargin; windowedY: windowedY;
1389 windowedWidth: appContainer.width - root.leftMargin; windowedHeight: windowedHeight }
1392 name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
1394 PropertyChanges { target: appDelegate; windowedX: windowedX; windowedY: PanelState.panelHeight;
1395 windowedWidth: windowedWidth; windowedHeight: appContainer.height - PanelState.panelHeight }
1398 name: "minimized"; when: appDelegate.minimized
1401 requestedX: -appDelegate.width / 2
1402 scale: units.gu(5) / appDelegate.width
1404 visuallyMinimized: true
1405 visuallyMaximized: false
1411 from: "staged,stagedWithSideStage"; to: "normal"
1412 enabled: appDelegate.animationsEnabled
1413 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
1414 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,opacity,requestedWidth,requestedHeight,scale"; duration: priv.animationDuration }
1417 from: "normal,restored,maximized,maximizedHorizontally,maximizedVertically,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedBottomLeft,maximizedTopRight,maximizedBottomRight";
1418 to: "staged,stagedWithSideStage"
1419 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,requestedWidth,requestedHeight"; duration: priv.animationDuration}
1423 enabled: appDelegate.animationsEnabled
1424 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1425 SequentialAnimation {
1426 UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
1427 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1432 // DecoratedWindow wants the scaleToPreviewSize set before enabling scaleToPreview
1433 PropertyAction { target: appDelegate; properties: "z,visible" }
1434 PropertyAction { target: decoratedWindow; property: "scaleToPreviewSize" }
1435 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,height"; duration: priv.animationDuration }
1436 UbuntuNumberAnimation { target: decoratedWindow; properties: "width,height,itemScale,angle,scaleToPreviewProgress"; duration: priv.animationDuration }
1437 UbuntuNumberAnimation { target: windowInfoItem; properties: "opacity"; duration: priv.animationDuration }
1440 from: "normal,staged"; to: "stagedWithSideStage"
1441 UbuntuNumberAnimation { target: appDelegate; properties: "x,y"; duration: priv.animationDuration }
1442 UbuntuNumberAnimation { target: appDelegate; properties: "requestedWidth,requestedHeight"; duration: priv.animationDuration }
1445 to: "windowedRightEdge"
1448 windowedRightEdgeMaths.startX = appDelegate.requestedX
1449 windowedRightEdgeMaths.startY = appDelegate.requestedY
1452 var thisRect = { x: appDelegate.windowedX, y: appDelegate.windowedY, width: appDelegate.requestedWidth, height: appDelegate.requestedHeight }
1453 var otherDelegate = appRepeater.itemAt(0);
1454 var otherRect = { x: otherDelegate.windowedX, y: otherDelegate.windowedY, width: otherDelegate.requestedWidth, height: otherDelegate.requestedHeight }
1455 var intersectionRect = MathUtils.intersectionRect(thisRect, otherRect)
1456 var mappedInterSectionRect = appDelegate.mapFromItem(root, intersectionRect.x, intersectionRect.y)
1457 opacityEffect.maskX = mappedInterSectionRect.x
1458 opacityEffect.maskY = mappedInterSectionRect.y
1459 opacityEffect.maskWidth = intersectionRect.width
1460 opacityEffect.maskHeight = intersectionRect.height
1466 from: "stagedRightEdge"; to: "staged"
1467 enabled: rightEdgeDragArea.cancelled // only transition back to state if the gesture was cancelled, in the other cases we play the focusAnimations.
1468 SequentialAnimation {
1470 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,height,width,scale"; duration: priv.animationDuration }
1471 UbuntuNumberAnimation { target: decoratedWindow; properties: "width,height,itemScale,angle,scaleToPreviewProgress"; duration: priv.animationDuration }
1473 // We need to release scaleToPreviewSize at last
1474 PropertyAction { target: decoratedWindow; property: "scaleToPreviewSize" }
1475 PropertyAction { target: appDelegate; property: "visible" }
1479 id: windowedTransition
1480 from: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen,minimized"
1481 to: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1482 enabled: appDelegate.animationsEnabled
1483 SequentialAnimation {
1484 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1485 UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,windowedX,windowedY,opacity,scale,requestedWidth,requestedHeight,windowedWidth,windowedHeight";
1486 duration: priv.animationDuration }
1487 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1488 ScriptAction { script: { fakeRectangle.stop(); } }
1495 property: "buttonsAlwaysVisible"
1496 value: appDelegate && appDelegate.maximized && touchControls.overlayShown
1501 objectName: "windowResizeArea"
1503 anchors.fill: appDelegate
1505 // workaround so that it chooses the correct resize borders when you drag from a corner ResizeGrip
1506 anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
1509 minimumY: PanelState.panelHeight // disallow resizing up past Panel
1510 minWidth: units.gu(10)
1511 minHeight: units.gu(10)
1512 borderThickness: units.gu(2)
1517 appDelegate.activate();
1523 objectName: "decoratedWindow"
1524 anchors.left: appDelegate.left
1525 anchors.top: appDelegate.top
1526 application: model.application
1527 surface: model.window.surface
1528 active: appDelegate.focus
1530 interactive: root.interactive
1532 maximizeButtonShown: appDelegate.canBeMaximized
1533 overlayShown: touchControls.overlayShown
1534 width: implicitWidth
1535 height: implicitHeight
1536 highlightSize: windowInfoItem.iconMargin / 2
1537 stageWidth: appContainer.width
1538 stageHeight: appContainer.height
1540 requestedWidth: appDelegate.requestedWidth
1541 requestedHeight: appDelegate.requestedHeight
1543 property int oldRequestedWidth: -1
1544 property int oldRequestedHeight: -1
1546 onRequestedWidthChanged: oldRequestedWidth = requestedWidth
1547 onRequestedHeightChanged: oldRequestedHeight = requestedHeight
1549 onCloseClicked: { appDelegate.close(); }
1550 onMaximizeClicked: {
1551 if (appDelegate.canBeMaximized) {
1552 appDelegate.anyMaximized ? appDelegate.requestRestore() : appDelegate.requestMaximize();
1555 onMaximizeHorizontallyClicked: {
1556 if (appDelegate.canBeMaximizedHorizontally) {
1557 appDelegate.maximizedHorizontally ? appDelegate.requestRestore() : appDelegate.requestMaximizeHorizontally()
1560 onMaximizeVerticallyClicked: {
1561 if (appDelegate.canBeMaximizedVertically) {
1562 appDelegate.maximizedVertically ? appDelegate.requestRestore() : appDelegate.requestMaximizeVertically()
1565 onMinimizeClicked: { appDelegate.requestMinimize(); }
1566 onDecorationPressed: { appDelegate.activate(); }
1567 onDecorationReleased: fakeRectangle.commit();
1569 property real angle: 0
1570 property real itemScale: 1
1574 origin.y: decoratedWindow.implicitHeight / 2
1575 xScale: decoratedWindow.itemScale
1576 yScale: decoratedWindow.itemScale
1579 origin { x: 0; y: (decoratedWindow.height / 2) }
1580 axis { x: 0; y: 1; z: 0 }
1581 angle: decoratedWindow.angle
1588 anchors.fill: decoratedWindow
1591 WindowControlsOverlay {
1596 stageWidth: appContainer.width
1597 stageHeight: appContainer.height
1599 onFakeMaximizeAnimationRequested: if (!appDelegate.maximized) fakeRectangle.maximize(amount, true)
1600 onFakeMaximizeLeftAnimationRequested: if (!appDelegate.maximizedLeft) fakeRectangle.maximizeLeft(amount, true)
1601 onFakeMaximizeRightAnimationRequested: if (!appDelegate.maximizedRight) fakeRectangle.maximizeRight(amount, true)
1602 onFakeMaximizeTopLeftAnimationRequested: if (!appDelegate.maximizedTopLeft) fakeRectangle.maximizeTopLeft(amount, true);
1603 onFakeMaximizeTopRightAnimationRequested: if (!appDelegate.maximizedTopRight) fakeRectangle.maximizeTopRight(amount, true);
1604 onFakeMaximizeBottomLeftAnimationRequested: if (!appDelegate.maximizedBottomLeft) fakeRectangle.maximizeBottomLeft(amount, true);
1605 onFakeMaximizeBottomRightAnimationRequested: if (!appDelegate.maximizedBottomRight) fakeRectangle.maximizeBottomRight(amount, true);
1606 onStopFakeAnimation: fakeRectangle.stop();
1607 onDragReleased: fakeRectangle.commit();
1610 WindowedFullscreenPolicy {
1611 id: windowedFullscreenPolicy
1612 active: root.mode == "windowed"
1613 surface: model.window.surface
1615 StagedFullscreenPolicy {
1616 id: stagedFullscreenPolicy
1617 active: root.mode == "staged" || root.mode == "stagedWithSideStage"
1618 surface: model.window.surface
1621 SpreadDelegateInputArea {
1623 objectName: "dragArea"
1624 anchors.fill: decoratedWindow
1626 closeable: !appDelegate.isDash
1629 spreadItem.highlightedIndex = index;
1630 if (distance == 0) {
1631 model.window.activate();
1632 priv.goneToSpread = false;
1636 priv.closingIndex = index
1637 model.window.close();
1643 objectName: "windowInfoItem"
1644 anchors { left: parent.left; top: decoratedWindow.bottom; topMargin: units.gu(1) }
1645 title: model.application.name
1646 iconSource: model.application.icon
1647 height: spreadItem.appInfoHeight
1650 visible: opacity > 0
1652 var nextApp = appRepeater.itemAt(index + 1);
1654 return Math.max(iconHeight, nextApp.x - appDelegate.x - units.gu(1))
1656 return appDelegate.width;
1660 spreadItem.highlightedIndex = index;
1661 priv.goneToSpread = false;
1667 objectName: "closeMouseArea"
1668 anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset }
1669 readonly property var mousePos: hoverMouseArea.mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1670 visible: !appDelegate.isDash && dragArea.distance == 0
1671 && index == spreadItem.highlightedIndex
1672 && mousePos.y < (decoratedWindow.height / 3)
1673 && mousePos.y > -units.gu(4)
1674 && mousePos.x > -units.gu(4)
1675 && mousePos.x < (decoratedWindow.width * 2 / 3)
1680 priv.closingIndex = index;
1681 appDelegate.close();
1685 source: "graphics/window-close.svg"
1686 anchors.fill: closeMouseArea
1687 anchors.margins: units.gu(2)
1688 sourceSize.width: width
1689 sourceSize.height: height
1696 FakeMaximizeDelegate {
1698 target: priv.focusedAppDelegate
1699 leftMargin: root.leftMargin
1700 appContainerWidth: appContainer.width
1701 appContainerHeight: appContainer.height
1707 // NB: it does its own positioning according to the specified edge
1710 onPassed: priv.goneToSpread = true;
1712 material: Component {
1715 width: parent.height
1716 height: parent.width
1718 anchors.centerIn: parent
1719 gradient: Gradient {
1720 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
1721 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
1730 objectName: "hoverMouseArea"
1731 anchors.fill: appContainer
1732 propagateComposedEvents: true
1737 property int scrollAreaWidth: width / 3
1738 property bool progressiveScrollingEnabled: false
1741 mouse.accepted = false
1743 if (hoverMouseArea.pressed) {
1747 // Find the hovered item and mark it active
1748 for (var i = appRepeater.count - 1; i >= 0; i--) {
1749 var appDelegate = appRepeater.itemAt(i);
1750 var mapped = mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
1751 var itemUnder = appDelegate.childAt(mapped.x, mapped.y);
1752 if (itemUnder && (itemUnder.objectName === "dragArea" || itemUnder.objectName === "windowInfoItem" || itemUnder.objectName == "closeMouseArea")) {
1753 spreadItem.highlightedIndex = i;
1758 if (floatingFlickable.contentWidth > floatingFlickable.width) {
1759 var margins = floatingFlickable.width * 0.05;
1761 if (!progressiveScrollingEnabled && mouseX < floatingFlickable.width - scrollAreaWidth) {
1762 progressiveScrollingEnabled = true
1765 // do we need to scroll?
1766 if (mouseX < scrollAreaWidth + margins) {
1767 var progress = Math.min(1, (scrollAreaWidth + margins - mouseX) / (scrollAreaWidth - margins));
1768 var contentX = (1 - progress) * (floatingFlickable.contentWidth - floatingFlickable.width)
1769 floatingFlickable.contentX = Math.max(0, Math.min(floatingFlickable.contentX, contentX))
1771 if (mouseX > floatingFlickable.width - scrollAreaWidth && progressiveScrollingEnabled) {
1772 var progress = Math.min(1, (mouseX - (floatingFlickable.width - scrollAreaWidth)) / (scrollAreaWidth - margins))
1773 var contentX = progress * (floatingFlickable.contentWidth - floatingFlickable.width)
1774 floatingFlickable.contentX = Math.min(floatingFlickable.contentWidth - floatingFlickable.width, Math.max(floatingFlickable.contentX, contentX))
1779 onPressed: mouse.accepted = false
1783 id: floatingFlickable
1784 objectName: "spreadFlickable"
1785 anchors.fill: appContainer
1787 contentWidth: spreadItem.spreadTotalWidth
1789 function snap(toIndex) {
1790 var delegate = appRepeater.itemAt(toIndex)
1791 var targetContentX = floatingFlickable.contentWidth / spreadItem.totalItemCount * toIndex;
1792 if (targetContentX - floatingFlickable.contentX > spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) {
1793 var offset = (spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) - (targetContentX - floatingFlickable.contentX)
1794 snapAnimation.to = floatingFlickable.contentX - offset;
1795 snapAnimation.start();
1796 } else if (targetContentX - floatingFlickable.contentX < spreadItem.leftStackXPos + units.gu(1)) {
1797 var offset = (spreadItem.leftStackXPos + units.gu(1)) - (targetContentX - floatingFlickable.contentX);
1798 snapAnimation.to = floatingFlickable.contentX - offset;
1799 snapAnimation.start();
1802 UbuntuNumberAnimation {id: snapAnimation; target: floatingFlickable; property: "contentX"}
1806 id: shortRightEdgeSwipeAnimation
1809 duration: priv.animationDuration
1813 id: rightEdgeDragArea
1814 objectName: "rightEdgeDragArea"
1815 direction: Direction.Leftwards
1816 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
1817 width: root.dragAreaWidth
1818 enabled: root.spreadEnabled
1820 property var gesturePoints: []
1821 property bool cancelled: false
1823 property real progress: -touchPosition.x / root.width
1824 onProgressChanged: {
1826 draggedProgress = progress;
1830 property real draggedProgress: 0
1832 onTouchPositionChanged: {
1833 gesturePoints.push(touchPosition.x);
1834 if (gesturePoints.length > 10) {
1835 gesturePoints.splice(0, gesturePoints.length - 10)
1839 onDraggingChanged: {
1841 // A potential edge-drag gesture has started. Start recording it
1844 draggedProgress = 0;
1846 // Ok. The user released. Did he drag far enough to go to full spread?
1847 if (gesturePoints[gesturePoints.length - 1] < -spreadItem.rightEdgeBreakPoint * spreadItem.width ) {
1849 // He dragged far enough, but if the last movement was a flick to the right again, he wants to cancel the spread again.
1850 var oneWayFlickToRight = true;
1851 var smallestX = gesturePoints[0]-1;
1852 for (var i = 0; i < gesturePoints.length; i++) {
1853 if (gesturePoints[i] <= smallestX) {
1854 oneWayFlickToRight = false;
1857 smallestX = gesturePoints[i];
1860 if (!oneWayFlickToRight) {
1861 // Ok, the user made it, let's go to spread!
1862 priv.goneToSpread = true;
1867 // Ok, the user didn't drag far enough to cross the breakPoint
1868 // Find out if it was a one-way movement to the left, in which case we just switch directly to next app.
1869 var oneWayFlick = true;
1870 var smallestX = rightEdgeDragArea.width;
1871 for (var i = 0; i < gesturePoints.length; i++) {
1872 if (gesturePoints[i] >= smallestX) {
1873 oneWayFlick = false;
1876 smallestX = gesturePoints[i];
1879 if (appRepeater.count > 1 &&
1880 (oneWayFlick && rightEdgeDragArea.distance > units.gu(2) || rightEdgeDragArea.distance > spreadItem.rightEdgeBreakPoint * spreadItem.width)) {
1881 var nextStage = appRepeater.itemAt(priv.nextInStack).stage
1882 for (var i = 0; i < appRepeater.count; i++) {
1883 if (i != priv.nextInStack && appRepeater.itemAt(i).stage == nextStage) {
1884 appRepeater.itemAt(i).playHidingAnimation()
1888 appRepeater.itemAt(priv.nextInStack).playFocusAnimation()
1889 if (appRepeater.itemAt(priv.nextInStack).stage == ApplicationInfoInterface.SideStage && !sideStage.shown) {
1903 TabletSideStageTouchGesture {
1905 objectName: "triGestureArea"
1906 anchors.fill: parent
1908 property Item appDelegate
1910 dragComponent: dragComponent
1911 dragComponentProperties: { "appDelegate": appDelegate }
1914 function matchDelegate(obj) { return String(obj.objectName).indexOf("appDelegate") >= 0; }
1916 var delegateAtCenter = Functions.itemAt(appContainer, x, y, matchDelegate);
1917 if (!delegateAtCenter) return;
1919 appDelegate = delegateAtCenter;
1923 if (sideStage.shown) {
1927 priv.updateMainAndSideStageIndexes()
1932 // If we're dragging to the sidestage.
1933 if (!sideStage.shown) {
1941 property Item appDelegate
1943 surface: appDelegate ? appDelegate.surface : null
1945 consumesInput: false
1948 requestedWidth: appDelegate.requestedWidth
1949 requestedHeight: appDelegate.requestedHeight
1952 height: units.gu(40)
1954 Drag.hotSpot.x: width/2
1955 Drag.hotSpot.y: height/2
1956 // only accept opposite stage.
1958 if (!surface) return "Disabled";
1959 if (appDelegate.isDash) return "Disabled";
1961 if (appDelegate.stage === ApplicationInfo.MainStage) {
1962 if (appDelegate.application.supportedOrientations
1963 & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {