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 Ubuntu.Gestures 0.1
20 import Unity.Application 0.1
23 import "../Components"
30 // Functions to be called from outside
31 function updateFocusedAppOrientation() {
32 var mainStageAppIndex = priv.indexOf(priv.mainStageAppId);
33 if (mainStageAppIndex >= 0 && mainStageAppIndex < spreadRepeater.count) {
34 spreadRepeater.itemAt(mainStageAppIndex).matchShellOrientation();
37 for (var i = 0; i < spreadRepeater.count; ++i) {
39 if (i === mainStageAppIndex) {
43 var spreadDelegate = spreadRepeater.itemAt(i);
45 var delta = spreadDelegate.appWindowOrientationAngle - root.shellOrientationAngle;
46 if (delta < 0) { delta += 360; }
49 var supportedOrientations = spreadDelegate.application.supportedOrientations;
50 if (supportedOrientations === Qt.PrimaryOrientation) {
51 supportedOrientations = spreadDelegate.orientations.primary;
54 if (delta === 180 && (supportedOrientations & spreadDelegate.shellOrientation)) {
55 spreadDelegate.matchShellOrientation();
59 function updateFocusedAppOrientationAnimated() {
60 var mainStageAppIndex = priv.indexOf(priv.mainStageAppId);
61 if (mainStageAppIndex >= 0 && mainStageAppIndex < spreadRepeater.count) {
62 spreadRepeater.itemAt(mainStageAppIndex).animateToShellOrientation();
65 if (priv.sideStageAppId) {
66 var sideStageAppIndex = priv.indexOf(priv.sideStageAppId);
67 if (sideStageAppIndex >= 0 && sideStageAppIndex < spreadRepeater.count) {
68 spreadRepeater.itemAt(sideStageAppIndex).matchShellOrientation();
73 function pushRightEdge(amount) {
74 if (spreadView.contentX == -spreadView.shift) {
75 edgeBarrier.push(amount);
79 orientationChangesEnabled: priv.mainAppOrientationChangesEnabled
81 supportedOrientations: mainApp ? mainApp.supportedOrientations
82 : (Qt.PortraitOrientation | Qt.LandscapeOrientation
83 | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation)
86 spreadView.selectedIndex = -1;
88 spreadView.contentX = -spreadView.shift;
91 onShellOrientationChanged: {
92 if (shellOrientation == Qt.PortraitOrientation || shellOrientation == Qt.InvertedPortraitOrientation) {
93 ApplicationManager.focusApplication(priv.mainStageAppId);
94 priv.sideStageAppId = "";
98 onInverseProgressChanged: {
99 // This can't be a simple binding because that would be triggered after this handler
100 // while we need it active before doing the anition left/right
101 spreadView.animateX = (inverseProgress == 0)
102 if (inverseProgress == 0 && priv.oldInverseProgress > 0) {
103 // left edge drag released. Minimum distance is given by design.
104 if (priv.oldInverseProgress > units.gu(22)) {
105 ApplicationManager.requestFocusApplication("unity8-dash");
108 priv.oldInverseProgress = inverseProgress;
111 onAltTabPressedChanged: {
112 if (!spreadEnabled) {
116 priv.highlightIndex = Math.min(spreadRepeater.count - 1, 1);
117 spreadView.snapToSpread();
119 for (var i = 0; i < spreadRepeater.count; i++) {
120 if (spreadRepeater.itemAt(i).zIndex === priv.highlightIndex) {
121 spreadView.snapTo(i);
129 focus: root.altTabPressed
134 priv.highlightIndex = (priv.highlightIndex + 1) % spreadRepeater.count
137 priv.highlightIndex = (priv.highlightIndex + spreadRepeater.count - 1) % spreadRepeater.count
145 objectName: "stagesPriv"
147 property string focusedAppId: ApplicationManager.focusedApplicationId
148 readonly property var focusedAppDelegate: {
149 var index = indexOf(focusedAppId);
150 return index >= 0 && index < spreadRepeater.count ? spreadRepeater.itemAt(index) : null
153 property string oldFocusedAppId: ""
154 property bool mainAppOrientationChangesEnabled: false
156 property real landscapeHeight: root.orientations.native_ == Qt.LandscapeOrientation ?
157 root.nativeHeight : root.nativeWidth
159 property bool shellIsLandscape: root.shellOrientation === Qt.LandscapeOrientation
160 || root.shellOrientation === Qt.InvertedLandscapeOrientation
162 property string mainStageAppId
163 property string sideStageAppId
165 // For convenience, keep properties of the first two apps in the model
166 property string appId0
167 property string appId1
169 property int oldInverseProgress: 0
171 property int highlightIndex: 0
173 onFocusedAppIdChanged: {
174 if (priv.focusedAppId.length > 0) {
175 var focusedApp = ApplicationManager.findApplication(focusedAppId);
176 if (focusedApp.stage == ApplicationInfoInterface.SideStage) {
177 priv.sideStageAppId = focusedAppId;
179 priv.mainStageAppId = focusedAppId;
180 root.mainApp = focusedApp;
184 appId0 = ApplicationManager.count >= 1 ? ApplicationManager.get(0).appId : "";
185 appId1 = ApplicationManager.count > 1 ? ApplicationManager.get(1).appId : "";
188 onFocusedAppDelegateChanged: {
189 if (focusedAppDelegate) {
190 focusedAppDelegate.focus = true;
194 property bool focusedAppDelegateIsDislocated: focusedAppDelegate &&
195 (focusedAppDelegate.dragOffset !== 0 || focusedAppDelegate.xTranslateAnimating)
196 function indexOf(appId) {
197 for (var i = 0; i < ApplicationManager.count; i++) {
198 if (ApplicationManager.get(i).appId == appId) {
205 function evaluateOneWayFlick(gesturePoints) {
206 // Need to have at least 3 points to recognize it as a flick
207 if (gesturePoints.length < 3) {
210 // Need to have a movement of at least 2 grid units to recognize it as a flick
211 if (Math.abs(gesturePoints[gesturePoints.length - 1] - gesturePoints[0]) < units.gu(2)) {
215 var oneWayFlick = true;
216 var smallestX = gesturePoints[0];
217 var leftWards = gesturePoints[1] < gesturePoints[0];
218 for (var i = 1; i < gesturePoints.length; i++) {
219 if ((leftWards && gesturePoints[i] >= smallestX)
220 || (!leftWards && gesturePoints[i] <= smallestX)) {
224 smallestX = gesturePoints[i];
229 onHighlightIndexChanged: {
230 spreadView.contentX = highlightIndex * spreadView.contentWidth / (spreadRepeater.count + 2)
235 target: ApplicationManager
237 if (spreadView.interactive) {
238 spreadView.snapTo(priv.indexOf(appId));
240 ApplicationManager.focusApplication(appId);
244 onApplicationAdded: {
245 if (spreadView.phase == 2) {
246 spreadView.snapTo(ApplicationManager.count - 1);
248 spreadView.phase = 0;
249 spreadView.contentX = -spreadView.shift;
250 ApplicationManager.focusApplication(appId);
254 onApplicationRemoved: {
255 if (priv.mainStageAppId == appId) {
256 ApplicationManager.focusApplication("unity8-dash")
258 if (priv.sideStageAppId == appId) {
259 priv.sideStageAppId = "";
262 if (ApplicationManager.count == 0) {
263 spreadView.phase = 0;
264 spreadView.contentX = -spreadView.shift;
265 } else if (spreadView.closingIndex == -1) {
266 // Unless we're closing the app ourselves in the spread,
267 // lets make sure the spread doesn't mess up by the changing app list.
268 spreadView.phase = 0;
269 spreadView.contentX = -spreadView.shift;
270 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
277 objectName: "spreadView"
279 interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
280 contentWidth: spreadRow.width - shift
283 property int tileDistance: units.gu(20)
284 property int sideStageWidth: units.gu(40)
285 property bool sideStageVisible: priv.sideStageAppId
287 // This indicates when the spreadView is active. That means, all the animations
288 // are activated and tiles need to line up for the spread.
289 readonly property bool active: shiftedContentX > 0 || spreadDragArea.dragging
291 // The flickable needs to fill the screen in order to get touch events all over.
292 // However, we don't want to the user to be able to scroll back all the way. For
293 // that, the beginning of the gesture starts with a negative value for contentX
294 // so the flickable wants to pull it into the view already. "shift" tunes the
295 // distance where to "lock" the content.
296 readonly property real shift: width / 2
297 readonly property real shiftedContentX: contentX + shift
299 // Phase of the animation:
300 // 0: Starting from right edge, a new app (index 1) comes in from the right
301 // 1: The app has reached the first snap position.
302 // 2: The list is dragged further and snaps into the spread view when entering phase 2
305 readonly property int phase0Width: sideStageWidth
306 readonly property int phase1Width: sideStageWidth
308 // Those markers mark the various positions in the spread (ratio to screen width from right to left):
309 // 0 - 1: following finger, snap back to the beginning on release
310 readonly property real positionMarker1: 0.2
311 // 1 - 2: curved snapping movement, snap to nextInStack on release
312 readonly property real positionMarker2: sideStageWidth / spreadView.width
313 // 2 - 3: movement follows finger, snaps to phase 2 (full spread) on release
314 readonly property real positionMarker3: 0.6
315 // passing 3, we detach movement from the finger and snap to phase 2 (full spread)
316 readonly property real positionMarker4: 0.8
318 readonly property int startSnapPosition: phase0Width * 0.5
319 readonly property int endSnapPosition: phase0Width * 0.75
320 readonly property real snapPosition: 0.75
322 property int selectedIndex: -1
323 property int draggedDelegateCount: 0
324 property int closingIndex: -1
326 // FIXME: Workaround Flickable's not keepping its contentX still when resized
327 onContentXChanged: { forceItToRemainStillIfBeingResized(); }
328 onShiftChanged: { forceItToRemainStillIfBeingResized(); }
329 function forceItToRemainStillIfBeingResized() {
330 if (root.beingResized && contentX != -shift) {
335 property bool animateX: true
336 property bool beingResized: root.beingResized
337 onBeingResizedChanged: {
339 // Brace yourselves for impact!
346 property bool sideStageDragging: sideStageDragHandle.dragging
347 property real sideStageDragProgress: sideStageDragHandle.progress
349 onSideStageDragProgressChanged: {
350 if (sideStageDragProgress == 1) {
351 ApplicationManager.focusApplication(priv.mainStageAppId);
352 priv.sideStageAppId = "";
356 // In case the ApplicationManager already holds an app when starting up we're missing animations
357 // Make sure we end up in the same state
358 Component.onCompleted: {
359 spreadView.contentX = -spreadView.shift
362 property int nextInStack: {
365 if (ApplicationManager.count > 1) {
369 case "mainAndOverlay":
370 if (ApplicationManager.count <= 2) {
373 if (priv.appId0 == priv.mainStageAppId || priv.appId0 == priv.sideStageAppId) {
374 if (priv.appId1 == priv.mainStageAppId || priv.appId1 == priv.sideStageAppId) {
385 property int nextZInStack: indexToZIndex(nextInStack)
394 State { // Side Stage only in overlay mode
397 State { // Main Stage and Side Stage in overlay mode
398 name: "mainAndOverlay"
400 State { // Main Stage and Side Stage in split mode
405 if (priv.mainStageAppId && !priv.sideStageAppId) {
408 if (!priv.mainStageAppId && priv.sideStageAppId) {
411 if (priv.mainStageAppId && priv.sideStageAppId) {
412 return "mainAndOverlay";
417 onShiftedContentXChanged: {
418 if (root.beingResized) {
419 // Flickabe.contentX wiggles during resizes. Don't react to it.
422 if (spreadView.phase == 0 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker2) {
423 spreadView.phase = 1;
424 } else if (spreadView.phase == 1 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker4) {
425 spreadView.phase = 2;
426 } else if (spreadView.phase == 1 && spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
427 spreadView.phase = 0;
432 if (shiftedContentX < phase0Width) {
433 snapAnimation.targetContentX = -shift;
434 snapAnimation.start();
435 } else if (shiftedContentX < phase1Width) {
442 function snapToSpread() {
443 // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
444 snapAnimation.targetContentX = (spreadView.width * spreadView.positionMarker4) + 1 - shift;
445 snapAnimation.start();
448 function snapTo(index) {
449 spreadView.selectedIndex = index;
450 snapAnimation.targetContentX = -shift;
451 snapAnimation.start();
454 // We need to shuffle z ordering a bit in order to keep side stage apps above main stage apps.
455 // We don't want to really reorder them in the model because that allows us to keep track
456 // of the last focused order.
457 function indexToZIndex(index) {
458 var app = ApplicationManager.get(index);
463 var active = app.appId == priv.mainStageAppId || app.appId == priv.sideStageAppId;
464 if (active && app.stage == ApplicationInfoInterface.MainStage) {
465 // if this app is active, and its the MainStage, always put it to index 0
468 if (active && app.stage == ApplicationInfoInterface.SideStage) {
469 if (!priv.mainStageAppId) {
470 // Only have SS apps running. Put the active one at 0
474 // Precondition now: There's an active MS app and this is SS app:
475 if (spreadView.nextInStack >= 0 && ApplicationManager.get(spreadView.nextInStack).stage == ApplicationInfoInterface.MainStage) {
476 // If the next app coming from the right is a MS app, we need to elevate this SS ap above it.
477 // Put it to at least level 2, or higher if there's more apps coming in before this one.
478 return Math.max(index, 2);
480 // if this is no next app to come in from the right, place this one at index 1, just on top the active MS app.
484 if (index <= 2 && app.stage == ApplicationInfoInterface.MainStage && priv.sideStageAppId) {
485 // Ok, this is an inactive MS app. If there's an active SS app around, we need to place this one
486 // in between the active MS app and the active SS app, so that it comes in from there when dragging from the right.
487 // If there's now active SS app, just leave it where it is.
488 return priv.indexOf(priv.sideStageAppId) < index ? index - 1 : index;
490 if (index == spreadView.nextInStack && app.stage == ApplicationInfoInterface.SideStage) {
491 // This is a SS app and the next one to come in from the right:
492 if (priv.sideStageAppId && priv.mainStageAppId) {
493 // If there's both, an active MS and an active SS app, put this one right on top of that
496 // Or if there's only one other active app, put it on top of that.
497 // The case that there isn't any other active app is already handled above.
500 if (index == 2 && spreadView.nextInStack == 1 && priv.sideStageAppId) {
501 // If its index 2 but not the next one to come in, it means
502 // we've pulled another one down to index 2. Move this one up to 2 instead.
505 // don't touch all others... (mostly index > 3 + simple cases where the above doesn't shuffle much)
509 SequentialAnimation {
511 property int targetContentX: -spreadView.shift
513 UbuntuNumberAnimation {
516 to: snapAnimation.targetContentX
517 duration: UbuntuAnimation.FastDuration
522 if (spreadView.selectedIndex >= 0) {
523 var newIndex = spreadView.selectedIndex;
524 spreadView.selectedIndex = -1;
525 ApplicationManager.focusApplication(ApplicationManager.get(newIndex).appId);
526 spreadView.phase = 0;
527 spreadView.contentX = -spreadView.shift;
533 Behavior on contentX {
534 enabled: root.altTabPressed
535 UbuntuNumberAnimation {}
540 x: spreadView.contentX
541 width: spreadView.width + Math.max(spreadView.width, ApplicationManager.count * spreadView.tileDistance)
545 spreadView.snapTo(0);
549 id: sideStageBackground
551 width: spreadView.sideStageWidth * (1 - sideStageDragHandle.progress)
552 height: priv.landscapeHeight
553 x: spreadView.width - width
554 z: spreadView.indexToZIndex(priv.indexOf(priv.sideStageAppId))
555 opacity: spreadView.phase == 0 ? 1 : 0
556 Behavior on opacity { UbuntuNumberAnimation {} }
560 id: sideStageDragHandle
561 anchors.right: sideStageBackground.left
562 anchors.top: sideStageBackground.top
564 height: priv.landscapeHeight
565 z: sideStageBackground.z
566 opacity: spreadView.phase <= 0 && spreadView.sideStageVisible ? 1 : 0
567 property real progress: 0
568 property bool dragging: false
570 Behavior on opacity { UbuntuNumberAnimation {} }
574 onSideStageVisibleChanged: {
575 if (spreadView.sideStageVisible) {
576 sideStageDragHandle.progress = 0;
582 anchors.centerIn: parent
583 width: sideStageDragHandleMouseArea.pressed ? parent.width * 2 : parent.width
584 height: parent.height
585 source: "graphics/sidestage_handle@20.png"
586 Behavior on width { UbuntuNumberAnimation {} }
590 id: sideStageDragHandleMouseArea
592 enabled: spreadView.shiftedContentX == 0
594 property var gesturePoints: new Array()
595 property real totalDiff
601 sideStageDragHandle.progress = 0;
602 sideStageDragHandle.dragging = true;
605 totalDiff += mouseX - startX;
606 if (priv.mainStageAppId) {
607 sideStageDragHandle.progress = Math.max(0, totalDiff / spreadView.sideStageWidth);
609 gesturePoints.push(mouseX);
612 if (priv.mainStageAppId) {
613 var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
614 sideStageDragSnapAnimation.to = sideStageDragHandle.progress > 0.5 || oneWayFlick ? 1 : 0;
615 sideStageDragSnapAnimation.start();
617 sideStageDragHandle.dragging = false;
621 UbuntuNumberAnimation {
622 id: sideStageDragSnapAnimation
623 target: sideStageDragHandle
628 sideStageDragHandle.dragging = false;
636 objectName: "spreadRepeater"
637 model: ApplicationManager
639 delegate: TransformedTabletSpreadDelegate {
641 objectName: model.appId ? "tabletSpreadDelegate_" + model.appId
642 : "tabletSpreadDelegate_null";
644 if (wantsMainStage) {
645 return spreadView.width;
647 return spreadView.sideStageWidth;
651 if (wantsMainStage) {
652 return spreadView.height;
654 return priv.landscapeHeight;
657 active: model.appId == priv.mainStageAppId || model.appId == priv.sideStageAppId
658 zIndex: spreadView.indexToZIndex(index)
659 selected: spreadView.selectedIndex == index
660 otherSelected: spreadView.selectedIndex >= 0 && !selected
661 isInSideStage: priv.sideStageAppId == model.appId
662 interactive: !spreadView.interactive && spreadView.phase === 0 && root.interactive
663 swipeToCloseEnabled: spreadView.interactive && !snapAnimation.running
664 maximizedAppTopMargin: root.maximizedAppTopMargin
665 dragOffset: !isDash && model.appId == priv.mainStageAppId && root.inverseProgress > 0 && spreadView.phase === 0 ? root.inverseProgress : 0
666 application: ApplicationManager.get(index)
668 highlightShown: root.altTabPressed && priv.highlightIndex == zIndex
670 readonly property bool wantsMainStage: model.stage == ApplicationInfoInterface.MainStage
672 readonly property bool isDash: model.appId == "unity8-dash"
675 target: spreadTile.application
676 property: "exemptFromLifecycle"
677 value: !model.isTouchApp || isExemptFromLifecycle(model.appId)
681 target: spreadTile.application
682 property: "requestedState"
683 value: (isDash && root.keepDashRunning)
684 || (!root.suspended && (model.appId == priv.mainStageAppId
685 || model.appId == priv.sideStageAppId))
686 ? ApplicationInfoInterface.RequestedRunning
687 : ApplicationInfoInterface.RequestedSuspended
690 // FIXME: A regular binding doesn't update any more after closing an app.
691 // Using a Binding for now.
695 value: (!spreadView.active && isDash && !active) ? -1 : spreadTile.zIndex
699 property real behavioredZIndex: zIndex
700 Behavior on behavioredZIndex {
701 enabled: spreadView.closingIndex >= 0
702 UbuntuNumberAnimation {}
705 // This is required because none of the bindings are triggered in some cases:
706 // When an app is closed, it might happen that ApplicationManager.get(nextInStack)
707 // returns a different app even though the nextInStackIndex and all the related
708 // bindings (index, mainStageApp, sideStageApp, etc) don't change. Let's force a
709 // binding update in that case.
711 target: ApplicationManager
712 onApplicationRemoved: spreadTile.z = Qt.binding(function() {
713 return spreadView.indexToZIndex(index);
718 var tileProgress = (spreadView.shiftedContentX - behavioredZIndex * spreadView.tileDistance) / spreadView.width;
719 // Some tiles (nextInStack, active) need to move directly from the beginning, normalize progress to immediately start at 0
720 if ((index == spreadView.nextInStack && spreadView.phase < 2) || (active && spreadView.phase < 1)) {
721 tileProgress += behavioredZIndex * spreadView.tileDistance / spreadView.width;
726 // TODO: Hiding tile when progress is such that it will be off screen.
727 property bool occluded: {
728 if (spreadView.active) return false;
729 else if (spreadTile.active) return false;
730 else if (xTranslateAnimating) return false;
731 else if (z <= 1 && priv.focusedAppDelegateIsDislocated) return false;
735 visible: Powerd.status == Powerd.On &&
736 !greeter.fullyShown &&
740 if (spreadView.phase == 0 && (spreadTile.active || spreadView.nextInStack == index)) {
741 if (progress < spreadView.positionMarker1) {
743 } else if (progress < spreadView.positionMarker1 + snappingCurve.period) {
744 return spreadView.positionMarker1 + snappingCurve.value * 3;
746 return spreadView.positionMarker2;
752 shellOrientationAngle: wantsMainStage ? root.shellOrientationAngle : 0
753 shellOrientation: wantsMainStage ? root.shellOrientation : Qt.PortraitOrientation
754 orientations: Orientations {
755 primary: spreadTile.wantsMainStage ? root.orientations.primary : Qt.PortraitOrientation
756 native_: spreadTile.wantsMainStage ? root.orientations.native_ : Qt.PortraitOrientation
757 portrait: root.orientations.portrait
758 invertedPortrait: root.orientations.invertedPortrait
759 landscape: root.orientations.landscape
760 invertedLandscape: root.orientations.invertedLandscape
764 if (spreadView.phase == 2) {
765 spreadView.snapTo(index);
771 spreadView.draggedDelegateCount++;
773 spreadView.draggedDelegateCount--;
778 spreadView.closingIndex = index;
779 ApplicationManager.stopApplication(ApplicationManager.get(index).appId);
784 when: model.appId == priv.mainStageAppId
785 property: "mainAppWindowOrientationAngle"
786 value: appWindowOrientationAngle
790 when: model.appId == priv.mainStageAppId
791 property: "mainAppOrientationChangesEnabled"
792 value: orientationChangesEnabled
797 type: EasingCurve.Linear
798 period: (spreadView.positionMarker2 - spreadView.positionMarker1) / 3
799 progress: spreadTile.progress - spreadView.positionMarker1
806 //eat touch events during the right edge gesture
809 enabled: spreadDragArea.dragging
812 DirectionalDragArea {
814 objectName: "spreadDragArea"
815 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
816 width: root.dragAreaWidth
817 direction: Direction.Leftwards
818 enabled: (spreadView.phase != 2 && root.spreadEnabled) || dragging
820 property var gesturePoints: new Array()
824 spreadView.phase = 0;
825 spreadView.contentX = -spreadView.shift;
829 var dragX = -touchX + spreadDragArea.width - spreadView.shift;
830 var maxDrag = spreadView.width * spreadView.positionMarker4 - spreadView.shift;
831 spreadView.contentX = Math.min(dragX, maxDrag);
833 gesturePoints.push(touchX);
838 // Gesture recognized. Start recording this gesture
841 // Ok. The user released. Find out if it was a one-way movement.
842 var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
845 if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
846 // If it was a short one-way movement, do the Alt+Tab switch
847 // no matter if we didn't cross positionMarker1 yet.
848 spreadView.snapTo(spreadView.nextInStack);
850 if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
852 } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
853 spreadView.snapTo(spreadView.nextInStack);
855 // otherwise snap to the closest snap position we can find
856 // (might be back to start, to app 1 or to spread)
867 // NB: it does its own positioning according to the specified edge
871 spreadView.snapToSpread();
873 material: Component {
879 anchors.centerIn: parent
881 GradientStop { position: 0.0; color: Qt.rgba(0.16,0.16,0.16,0.7)}
882 GradientStop { position: 1.0; color: Qt.rgba(0.16,0.16,0.16,0)}