@@ -46,6 +46,7 @@ FocusScope {
46 property int leftMargin: 0
46 property int leftMargin: 0
47 property bool oskEnabled: false
47 property bool oskEnabled: false
48 property rect inputMethodRect
48 property rect inputMethodRect
49+ property real rightEdgePushProgress: 0
50 // Configuration
51 // Configuration
51 property string mode: "staged"
52 property string mode: "staged"
@@ -100,7 +101,9 @@ FocusScope {
101 // No we didn't, do a quick alt-tab
102 // No we didn't, do a quick alt-tab
102 if (appRepeater.count > 1) {
103 if (appRepeater.count > 1) {
103− appRepeater.itemAt(1).claimFocus();
104+ appRepeater.itemAt(1).activate();
105+ } else if (appRepeater.count > 0) {
106+ appRepeater.itemAt(0).activate(); // quick alt-tab to the only (minimized) window should still activate it
@@ -125,11 +128,6 @@ FocusScope {
125 // functions to be called from outside
128 // functions to be called from outside
126 function updateFocusedAppOrientation() { /* TODO */ }
129 function updateFocusedAppOrientation() { /* TODO */ }
127 function updateFocusedAppOrientationAnimated() { /* TODO */}
130 function updateFocusedAppOrientationAnimated() { /* TODO */}
128− function pushRightEdge(amount) {
129− if (root.spreadEnabled) {
130− edgeBarrier.push(amount);
134 function closeSpread() {
132 function closeSpread() {
135 priv.goneToSpread = false;
133 priv.goneToSpread = false;
@@ -141,6 +139,12 @@ FocusScope {
142+ onRightEdgePushProgressChanged: {
143+ if (spreadEnabled && rightEdgePushProgress >= 1) {
144+ priv.goneToSpread = true
144 GSettings {
148 GSettings {
145 id: lifecycleExceptions
149 id: lifecycleExceptions
146 schema.id: "com.canonical.qtmir"
150 schema.id: "com.canonical.qtmir"
@@ -452,7 +456,7 @@ FocusScope {
452 PropertyChanges { target: wallpaper; visible: false }
456 PropertyChanges { target: wallpaper; visible: false }
455− name: "stagedRightEdge"; when: (rightEdgeDragArea.dragging || edgeBarrier.progress > 0) && root.mode == "staged"
459+ name: "stagedRightEdge"; when: root.spreadEnabled && (rightEdgeDragArea.dragging || rightEdgePushProgress > 0) && root.mode == "staged"
456 PropertyChanges {
460 PropertyChanges {
457 target: blurLayer;
461 target: blurLayer;
458 visible: true;
462 visible: true;
@@ -462,7 +466,7 @@ FocusScope {
465− name: "sideStagedRightEdge"; when: (rightEdgeDragArea.dragging || edgeBarrier.progress > 0) && root.mode == "stagedWithSideStage"
469+ name: "sideStagedRightEdge"; when: root.spreadEnabled && (rightEdgeDragArea.dragging || rightEdgePushProgress > 0) && root.mode == "stagedWithSideStage"
466 extend: "stagedRightEdge"
470 extend: "stagedRightEdge"
467 PropertyChanges {
471 PropertyChanges {
468 target: sideStage
472 target: sideStage
@@ -471,18 +475,18 @@ FocusScope {
474− name: "windowedRightEdge"; when: (rightEdgeDragArea.dragging || edgeBarrier.progress > 0) && root.mode == "windowed"
478+ name: "windowedRightEdge"; when: root.spreadEnabled && (rightEdgeDragArea.dragging || rightEdgePushProgress > 0) && root.mode == "windowed"
475 PropertyChanges {
479 PropertyChanges {
476 target: blurLayer;
480 target: blurLayer;
477 visible: true
481 visible: true
478 blurRadius: 32
482 blurRadius: 32
479 brightness: .65
483 brightness: .65
480− opacity: MathUtils.linearAnimation(spreadItem.rightEdgeBreakPoint, 1, 0, 1, Math.max(rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0, edgeBarrier.progress))
484+ opacity: MathUtils.linearAnimation(spreadItem.rightEdgeBreakPoint, 1, 0, 1, Math.max(rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0, rightEdgePushProgress))
484 name: "staged"; when: root.mode === "staged"
488 name: "staged"; when: root.mode === "staged"
485− PropertyChanges { target: wallpaper; visible: false }
489+ PropertyChanges { target: wallpaper; visible: !priv.focusedAppDelegate || priv.focusedAppDelegate.x !== 0 }
488 name: "stagedWithSideStage"; when: root.mode === "stagedWithSideStage"
492 name: "stagedWithSideStage"; when: root.mode === "stagedWithSideStage"
@@ -502,7 +506,6 @@ FocusScope {
502 Transition {
506 Transition {
503 to: "spread"
507 to: "spread"
504 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: appRepeater.count > 1 ? 1 : 0 }
508 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: appRepeater.count > 1 ? 1 : 0 }
505− PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
507 Transition {
510 Transition {
508 from: "spread"
511 from: "spread"
@@ -517,6 +520,7 @@ FocusScope {
519 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
522 PropertyAction { target: spreadItem; property: "highlightedIndex"; value: -1 }
523+ PropertyAction { target: floatingFlickable; property: "contentX"; value: 0 }
522 Transition {
526 Transition {
@@ -665,6 +669,13 @@ FocusScope {
673+ id: boundariesForWindowPlacement
674+ anchors.fill: parent
675+ anchors.topMargin: PanelState.panelHeight
668 Repeater {
679 Repeater {
669 id: appRepeater
680 id: appRepeater
670 model: topLevelSurfaceList
681 model: topLevelSurfaceList
@@ -804,7 +815,7 @@ FocusScope {
804 maximizedTopLeft || maximizedTopRight || maximizedBottomLeft || maximizedBottomRight
815 maximizedTopLeft || maximizedTopRight || maximizedBottomLeft || maximizedBottomRight
806 readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
817 readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
807− readonly property bool fullscreen: window.state === Mir.FullscreenState
818+ readonly property bool fullscreen: windowState === WindowStateStorage.WindowStateFullscreen
809 readonly property bool canBeMaximized: canBeMaximizedHorizontally && canBeMaximizedVertically
820 readonly property bool canBeMaximized: canBeMaximizedHorizontally && canBeMaximizedVertically
810 readonly property bool canBeMaximizedLeftRight: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
821 readonly property bool canBeMaximizedLeftRight: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
@@ -815,7 +826,10 @@ FocusScope {
815 readonly property bool canBeMaximizedVertically: maximumHeight == 0 || maximumHeight >= appContainer.height
826 readonly property bool canBeMaximizedVertically: maximumHeight == 0 || maximumHeight >= appContainer.height
816 readonly property alias orientationChangesEnabled: decoratedWindow.orientationChangesEnabled
827 readonly property alias orientationChangesEnabled: decoratedWindow.orientationChangesEnabled
829+ // TODO drop our own windowType once Mir/Miral/Qtmir gets in sync with ours
818 property int windowState: WindowStateStorage.WindowStateNormal
830 property int windowState: WindowStateStorage.WindowStateNormal
831+ property int prevWindowState: WindowStateStorage.WindowStateRestored
819 property bool animationsEnabled: true
833 property bool animationsEnabled: true
820 property alias title: decoratedWindow.title
834 property alias title: decoratedWindow.title
821 readonly property string appName: model.application ? model.application.name : ""
835 readonly property string appName: model.application ? model.application.name : ""
@@ -833,7 +847,6 @@ FocusScope {
833 readonly property var surface: model.window.surface
847 readonly property var surface: model.window.surface
834 readonly property var window: model.window
848 readonly property var window: model.window
836− readonly property alias resizeArea: resizeArea
837 readonly property alias focusedSurface: decoratedWindow.focusedSurface
850 readonly property alias focusedSurface: decoratedWindow.focusedSurface
838 readonly property bool dragging: touchControls.overlayShown ? touchControls.dragging : decoratedWindow.dragging
851 readonly property bool dragging: touchControls.overlayShown ? touchControls.dragging : decoratedWindow.dragging
@@ -871,9 +884,6 @@ FocusScope {
872 priv.updateMainAndSideStageIndexes();
885 priv.updateMainAndSideStageIndexes();
874− if (root.mode == "windowed") {
875− appDelegate.restore(true /* animated */, appDelegate.windowState);
877 appDelegate.focus = true;
887 appDelegate.focus = true;
@@ -890,7 +900,7 @@ FocusScope {
890 screenWidth: appContainer.width
900 screenWidth: appContainer.width
891 screenHeight: appContainer.height
901 screenHeight: appContainer.height
892 leftMargin: root.leftMargin
902 leftMargin: root.leftMargin
893− minimumY: PanelState.panelHeight
903+ minimumY: boundariesForWindowPlacement.y
896 Connections {
906 Connections {
@@ -923,7 +933,14 @@ FocusScope {
923 } else if (model.window.state === Mir.MaximizedBottomRightState) {
933 } else if (model.window.state === Mir.MaximizedBottomRightState) {
924 appDelegate.maximizeBottomRight();
934 appDelegate.maximizeBottomRight();
925 } else if (model.window.state === Mir.RestoredState) {
935 } else if (model.window.state === Mir.RestoredState) {
936+ if (appDelegate.fullscreen && appDelegate.prevWindowState != WindowStateStorage.WindowStateRestored) {
937+ model.window.requestState(WindowStateStorage.toMirState(appDelegate.prevWindowState));
926− appDelegate.restore();
939+ appDelegate.restore();
941+ } else if (model.window.state === Mir.FullscreenState) {
942+ appDelegate.prevWindowState = appDelegate.windowState;
943+ appDelegate.windowState = WindowStateStorage.WindowStateFullscreen;
@@ -1025,6 +1042,7 @@ FocusScope {
1025 animationsEnabled = (animated === undefined) || animated;
1042 animationsEnabled = (animated === undefined) || animated;
1026 windowState = state || WindowStateStorage.WindowStateRestored;
1043 windowState = state || WindowStateStorage.WindowStateRestored;
1027 windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
1044 windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
1045+ prevWindowState = windowState;
1030 function playFocusAnimation() {
1048 function playFocusAnimation() {
@@ -1187,7 +1205,7 @@ FocusScope {
1187 when: (root.mode == "staged" || root.mode == "stagedWithSideStage") && (root.state == "sideStagedRightEdge" || root.state == "stagedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running)
1205 when: (root.mode == "staged" || root.mode == "stagedWithSideStage") && (root.state == "sideStagedRightEdge" || root.state == "stagedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running)
1188 PropertyChanges {
1206 PropertyChanges {
1189 target: stagedRightEdgeMaths
1207 target: stagedRightEdgeMaths
1190− progress: Math.max(edgeBarrier.progress, rightEdgeDragArea.draggedProgress)
1208+ progress: Math.max(rightEdgePushProgress, rightEdgeDragArea.draggedProgress)
1192 PropertyChanges {
1210 PropertyChanges {
1193 target: appDelegate
1211 target: appDelegate
@@ -1214,11 +1232,11 @@ FocusScope {
1216 name: "windowedRightEdge"
1234 name: "windowedRightEdge"
1217− when: root.mode == "windowed" && (root.state == "windowedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running || edgeBarrier.progress > 0)
1235+ when: root.mode == "windowed" && (root.state == "windowedRightEdge" || rightEdgeFocusAnimation.running || hidingAnimation.running || rightEdgePushProgress > 0)
1218 PropertyChanges {
1236 PropertyChanges {
1219 target: windowedRightEdgeMaths
1237 target: windowedRightEdgeMaths
1220 swipeProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0
1238 swipeProgress: rightEdgeDragArea.dragging ? rightEdgeDragArea.progress : 0
1221− pushProgress: edgeBarrier.progress
1239+ pushProgress: rightEdgePushProgress
1223 PropertyChanges {
1241 PropertyChanges {
1224 target: appDelegate
1242 target: appDelegate
@@ -1412,11 +1430,12 @@ FocusScope {
1412 name: "minimized"; when: appDelegate.minimized
1430 name: "minimized"; when: appDelegate.minimized
1413 PropertyChanges {
1431 PropertyChanges {
1414 target: appDelegate
1432 target: appDelegate
1415− requestedX: -appDelegate.width / 2
1416 scale: units.gu(5) / appDelegate.width
1433 scale: units.gu(5) / appDelegate.width
1417 opacity: 0;
1434 opacity: 0;
1418 visuallyMinimized: true
1435 visuallyMinimized: true
1419 visuallyMaximized: false
1436 visuallyMaximized: false
1437+ x: -appDelegate.width / 2
@@ -1432,15 +1451,6 @@ FocusScope {
1432 to: "staged,stagedWithSideStage"
1451 to: "staged,stagedWithSideStage"
1433 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,requestedWidth,requestedHeight"; duration: priv.animationDuration}
1452 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,requestedWidth,requestedHeight"; duration: priv.animationDuration}
1437− enabled: appDelegate.animationsEnabled
1438− PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1439− SequentialAnimation {
1440− UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
1441− PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1444 Transition {
1454 Transition {
1445 to: "spread"
1455 to: "spread"
1446 // DecoratedWindow wants the scaleToPreviewSize set before enabling scaleToPreview
1456 // DecoratedWindow wants the scaleToPreviewSize set before enabling scaleToPreview
@@ -1489,6 +1499,31 @@ FocusScope {
1489 PropertyAction { target: appDelegate; property: "visible" }
1499 PropertyAction { target: appDelegate; property: "visible" }
1503+ from: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1505+ SequentialAnimation {
1506+ ScriptAction { script: print("transitioning:", appDelegate.x, appDelegate.y, appDelegate.scale) }
1507+ ScriptAction { script: { fakeRectangle.stop(); } }
1508+ PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1509+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,scale,opacity"; duration: priv.animationDuration }
1510+ PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1515+ to: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1516+ SequentialAnimation {
1517+ ScriptAction { script: print("transitioning:", appDelegate.x, appDelegate.y, appDelegate.scale) }
1518+ PropertyAction { target: appDelegate; property: "visuallyMinimized,z" }
1519+ ParallelAnimation {
1520+ UbuntuNumberAnimation { target: appDelegate; properties: "x"; from: -appDelegate.width / 2; duration: priv.animationDuration }
1521+ UbuntuNumberAnimation { target: appDelegate; properties: "y,opacity"; duration: priv.animationDuration }
1522+ UbuntuNumberAnimation { target: appDelegate; properties: "scale"; from: 0; duration: priv.animationDuration }
1524+ PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1492 Transition {
1527 Transition {
1493 id: windowedTransition
1528 id: windowedTransition
1494 from: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen,minimized"
1529 from: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen,minimized"
@@ -1520,7 +1555,7 @@ FocusScope {
1520 anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
1555 anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
1522 target: appDelegate
1557 target: appDelegate
1523− minimumY: PanelState.panelHeight // disallow resizing up past Panel
1558+ boundsItem: boundariesForWindowPlacement
1524 minWidth: units.gu(10)
1559 minWidth: units.gu(10)
1525 minHeight: units.gu(10)
1560 minHeight: units.gu(10)
1526 borderThickness: units.gu(2)
1561 borderThickness: units.gu(2)
@@ -1539,7 +1574,7 @@ FocusScope {
1539 anchors.top: appDelegate.top
1574 anchors.top: appDelegate.top
1540 application: model.application
1575 application: model.application
1541 surface: model.window.surface
1576 surface: model.window.surface
1542− active: appDelegate.focus
1577+ active: model.window.focused
1543 focus: true
1578 focus: true
1544 interactive: root.interactive
1579 interactive: root.interactive
1545 showDecoration: 1
1580 showDecoration: 1
@@ -1548,8 +1583,7 @@ FocusScope {
1548 width: implicitWidth
1583 width: implicitWidth
1549 height: implicitHeight
1584 height: implicitHeight
1550 highlightSize: windowInfoItem.iconMargin / 2
1585 highlightSize: windowInfoItem.iconMargin / 2
1551− stageWidth: appContainer.width
1586+ boundsItem: boundariesForWindowPlacement
1552− stageHeight: appContainer.height
1554 requestedWidth: appDelegate.requestedWidth
1588 requestedWidth: appDelegate.requestedWidth
1555 requestedHeight: appDelegate.requestedHeight
1589 requestedHeight: appDelegate.requestedHeight
@@ -1581,7 +1615,10 @@ FocusScope {
1581 onDecorationReleased: fakeRectangle.commit();
1615 onDecorationReleased: fakeRectangle.commit();
1583 property real angle: 0
1617 property real angle: 0
1618+ Behavior on angle { enabled: priv.closingIndex >= 0; UbuntuNumberAnimation {} }
1584 property real itemScale: 1
1619 property real itemScale: 1
1620+ Behavior on itemScale { enabled: priv.closingIndex >= 0; UbuntuNumberAnimation {} }
1585 transform: [
1622 transform: [
1587 origin.x: 0
1624 origin.x: 0
@@ -1604,11 +1641,12 @@ FocusScope {
1605 WindowControlsOverlay {
1642 WindowControlsOverlay {
1606 id: touchControls
1643 id: touchControls
1644+ anchors.fill: appDelegate
1607 target: appDelegate
1645 target: appDelegate
1646+ resizeArea: resizeArea
1608 enabled: false
1647 enabled: false
1609 visible: enabled
1648 visible: enabled
1610− stageWidth: appContainer.width
1649+ boundsItem: boundariesForWindowPlacement
1611− stageHeight: appContainer.height
1613 onFakeMaximizeAnimationRequested: if (!appDelegate.maximized) fakeRectangle.maximize(amount, true)
1651 onFakeMaximizeAnimationRequested: if (!appDelegate.maximized) fakeRectangle.maximize(amount, true)
1614 onFakeMaximizeLeftAnimationRequested: if (!appDelegate.maximizedLeft) fakeRectangle.maximizeLeft(amount, true)
1652 onFakeMaximizeLeftAnimationRequested: if (!appDelegate.maximizedLeft) fakeRectangle.maximizeLeft(amount, true)
@@ -1642,7 +1680,6 @@ FocusScope {
1642 onClicked: {
1680 onClicked: {
1643 spreadItem.highlightedIndex = index;
1681 spreadItem.highlightedIndex = index;
1644 if (distance == 0) {
1682 if (distance == 0) {
1645− model.window.activate();
1646 priv.goneToSpread = false;
1683 priv.goneToSpread = false;
@@ -1703,6 +1740,41 @@ FocusScope {
1703 sourceSize.height: height
1740 sourceSize.height: height
1745+ // Group all child windows in this item so that we can fade them out together when going to the spread
1746+ // (and fade them in back again when returning from it)
1747+ readonly property bool stageOnProperState: root.state === "windowed"
1748+ || root.state === "staged"
1749+ || root.state === "stagedWithSideStage"
1751+ // TODO: Is it worth the extra cost of layering to avoid the opacity artifacts of intersecting children?
1752+ // Btw, will involve more than uncommenting the line below as children won't necessarily fit this item's
1753+ // geometry. This is just a reference.
1754+ //layer.enabled: opacity !== 0.0 && opacity !== 1.0
1756+ opacity: stageOnProperState ? 1.0 : 0.0
1757+ visible: opacity !== 0.0 // make it transparent to input as well
1758+ Behavior on opacity { UbuntuNumberAnimation {} }
1761+ id: childWindowRepeater
1762+ model: appDelegate.surface ? appDelegate.surface.childSurfaceList : null
1764+ delegate: ChildWindowTree {
1765+ surface: model.surface
1767+ // Account for the displacement caused by window decoration in the top-level surface
1768+ // Ie, the top-level surface is not positioned at (0,0) of this ChildWindow's parent (appDelegate)
1769+ displacementX: appDelegate.clientAreaItem.x
1770+ displacementY: appDelegate.clientAreaItem.y
1772+ boundsItem: boundariesForWindowPlacement
1774+ z: childWindowRepeater.count - model.index
@@ -1715,30 +1787,6 @@ FocusScope {
1715 appContainerHeight: appContainer.height
1787 appContainerHeight: appContainer.height
1721− // NB: it does its own positioning according to the specified edge
1724− onPassed: priv.goneToSpread = true;
1726− material: Component {
1729− width: parent.height
1730− height: parent.width
1732− anchors.centerIn: parent
1733− gradient: Gradient {
1734− GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.5)}
1735− GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}
1742 MouseArea {
1790 MouseArea {
1743 id: hoverMouseArea
1791 id: hoverMouseArea
1744 objectName: "hoverMouseArea"
1792 objectName: "hoverMouseArea"
@@ -1805,11 +1853,11 @@ FocusScope {
1805 var targetContentX = floatingFlickable.contentWidth / spreadItem.totalItemCount * toIndex;
1853 var targetContentX = floatingFlickable.contentWidth / spreadItem.totalItemCount * toIndex;
1806 if (targetContentX - floatingFlickable.contentX > spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) {
1854 if (targetContentX - floatingFlickable.contentX > spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) {
1807 var offset = (spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) - (targetContentX - floatingFlickable.contentX)
1855 var offset = (spreadItem.rightStackXPos - (spreadItem.spreadItemWidth / 2)) - (targetContentX - floatingFlickable.contentX)
1808− snapAnimation.to = floatingFlickable.contentX - offset;
1856+ snapAnimation.to = Math.max(0, floatingFlickable.contentX - offset);
1809 snapAnimation.start();
1857 snapAnimation.start();
1810 } else if (targetContentX - floatingFlickable.contentX < spreadItem.leftStackXPos + units.gu(1)) {
1858 } else if (targetContentX - floatingFlickable.contentX < spreadItem.leftStackXPos + units.gu(1)) {
1811 var offset = (spreadItem.leftStackXPos + units.gu(1)) - (targetContentX - floatingFlickable.contentX);
1859 var offset = (spreadItem.leftStackXPos + units.gu(1)) - (targetContentX - floatingFlickable.contentX);
1812− snapAnimation.to = floatingFlickable.contentX - offset;
1860+ snapAnimation.to = Math.max(0, floatingFlickable.contentX - offset);
1813 snapAnimation.start();
1861 snapAnimation.start();