@@ -188,25 +188,34 @@ Item {
190 EdgeDragArea {
190 EdgeDragArea {
191− id: coverFlipDragArea
191+ id: spreadDragArea
192 direction: Direction.Leftwards
192 direction: Direction.Leftwards
194 //enabled: root.available
194 //enabled: root.available
195 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
195 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
196 width: root.dragAreaWidth
196 width: root.dragAreaWidth
198+ property bool attachedToView: true
198 onTouchXChanged: {
200 onTouchXChanged: {
199 if (!dragging && !priv.waitingForScreenshot) {
201 if (!dragging && !priv.waitingForScreenshot) {
200 priv.requestNewScreenshot();
202 priv.requestNewScreenshot();
203+ spreadView.stage = 0;
204+ attachedToView = true;
206+ if (dragging && !priv.waitingForScreenshot && attachedToView) {
207+ spreadView.contentX = -touchX
202− if (dragging && !priv.waitingForScreenshot) {
209+ if (attachedToView && spreadView.contentX >= spreadView.width * spreadView.positionMarker3) {
203− coverFlickable.contentX = -touchX
210+ attachedToView = false;
207 onDraggingChanged: {
215 onDraggingChanged: {
216+ print("Dragging changed", dragging)
208− if (!dragging) {
217+ if (!dragging && attachedToView) {
209− coverFlip.snap();
218+ spreadView.snap();
@@ -215,372 +224,195 @@ Item {
215 id: coverFlipBackground
224 id: coverFlipBackground
216 anchors.fill: parent
225 anchors.fill: parent
217 color: "black"
226 color: "black"
218− visible: coverFlip.visible
227+ visible: spreadView.visible
221 InputFilterArea {
230 InputFilterArea {
222 anchors.fill: root
231 anchors.fill: root
223− blockInput: coverFlip.visible
232+ blockInput: spreadView.visible
226 Flickable {
235 Flickable {
227− id: coverFlickable
236+ id: spreadView
228− anchors.fill: root
237+ anchors.fill: parent
229− contentHeight: height
238+ visible: contentX > 0
230− contentWidth: width * 2 + ((ApplicationManager.count - 2) * root.width * coverFlip.tileDistance * 1.5)
239+ contentWidth: spreadRow.width
231− flickableDirection: Qt.Horizontal
232− enabled: coverFlip.visible
234− property bool passedFirstStage: false
241+ property int tileDistance: width / 4
243+ property real positionMarker1: 0.25
244+ property real positionMarker2: 0.5
245+ property real positionMarker3: 0.7
246+ property real positionMarker4: 1.25
248+ // Stage of the animation:
249+ // 0: Starting from right edge, a new app (index 1) comes in from the right
250+ // 1: The app has reached fullscreen. The list is reordered and shifted to the right in order to have the new app the leftmost one
251+ // 2: The list is dragged further and snaps into the spread view when entering stage 2
252+ property int stage: 0
236 onContentXChanged: {
254 onContentXChanged: {
237− if (coverFlickable.passedFirstStage && contentX < width * coverFlip.progressMarker2) {
255+ switch (stage) {
238− contentX = width * coverFlip.progressMarker2;
256+ case 0:
239− return;
257+ if (contentX > width * positionMarker2) {
240− }
258+ print("switching to app 1 .")
241− var progress = contentX / width
259+ ApplicationManager.focusApplication(ApplicationManager.get(1).appId)
242− if (progress > coverFlip.progressMarker2) {
260+ stage = 1;
243− coverFlickable.passedFirstStage = true;
261+ }
264+ if (contentX < width * positionMarker2) {
265+ print("switching to app 1 -")
266+ ApplicationManager.focusApplication(ApplicationManager.get(1).appId)
268+ } else if (contentX >= width * positionMarker4) {
245− coverFlip.progress = progress;
248− Row {
274+ function snap() {
249− id: coverFlip
275+ if (contentX < positionMarker1 * width) {
250− height: parent.height
276+ snapAnimation.targetContentX = 0;
251− // The MouseAreas on the AppImages need to be children of the flickable in order to make focus stealing
277+ snapAnimation.start();
252− // for flicking vs. clicking work correctly.
278+ } else if (contentX < positionMarker2 * width) {
253− // However, for the animation calculations to become easier we don't want the row to move so we don't
279+ snapTo(1)
254− // always have to take contentX into account. So lets just compensate the flickable's movement here.
280+ } else if (contentX < positionMarker3 * width) {
255− x: coverFlickable.contentX
281+ snapAnimation.targetContentX = width * positionMarker2 + 1;
256− visible: progress > 0
282+ snapAnimation.start();
257−
283+ } else if (stage < 2){
258− property real progress: 0
284+ snapAnimation.targetContentX = width * positionMarker4;
259− property real startAngle: 45
285+ snapAnimation.start();
260− property int endAngle: 10
262− property real maxScale: 1.4
263− property real minScale: .6
265− // Markers: relative screen position from right to left
266− // marks the line where first application is finished moving in from the right
267− property real progressMarker1: 0.15
268− property real progressMarker2: 0.5
270− property real tileDistance: 0.1
272− property bool animatingBack: false
274− property real tileWidth: root.width
276− property real oldProgress: 0
277− onProgressChanged: {
278− if (coverFlipDragArea.dragging) {
279− if (oldProgress < coverFlip.progressMarker2 && progress >= coverFlip.progressMarker2) {
280− ApplicationManager.move(0, 1)
281− } else if (oldProgress >= coverFlip.progressMarker2 && progress < coverFlip.progressMarker2) {
282− ApplicationManager.move(0, 1)
285− oldProgress = progress;
288+ function snapTo(index) {
289+ snapAnimation.targetContentX = width * positionMarker2;
290+ snapAnimation.toIndex = index;
291+ snapAnimation.start();
288− function snap() {
294+ SequentialAnimation {
289− if (coverFlip.progress < coverFlip.progressMarker1) {
295+ id: snapAnimation
290− snapAnimation.targetContentX = 0
296+ property int targetContentX: 0
291− snapAnimation.targetAppId = ApplicationManager.get(0).appId;
297+ property int toIndex: -1
292− } else if (coverFlip.progress < coverFlip.progressMarker2) {
298+
293− snapAnimation.targetContentX = root.width * coverFlip.progressMarker2
299+ UbuntuNumberAnimation {
294− snapAnimation.targetAppId = ApplicationManager.get(1).appId;
300+ target: spreadView
295− } else if (coverFlip.progress < 0.6) {
301+ property: "contentX"
296− snapAnimation.targetContentX = root.width * coverFlip.progressMarker2
302+ to: snapAnimation.targetContentX
297− snapAnimation.targetAppId = ApplicationManager.get(0).appId;
303+ duration: UbuntuAnimation.SnapDuration
299− if (ApplicationManager.count == 3) {
305+ ScriptAction {
300− snapAnimation.targetContentX = root.width * 1.1;
306+ script: {
301− } else {
307+ print("animation finished: stage", spreadView.stage, "contentX", spreadView.contentX, "focused app", ApplicationManager.get(0).appId, ApplicationManager.get(1).appId)
302− snapAnimation.targetContentX = root.width * 1.25;
308+ if (snapAnimation.toIndex >= 0) {
309+ print("switching to app", snapAnimation.toIndex, ApplicationManager.get(snapAnimation.toIndex).name)
310+ ApplicationManager.focusApplication(ApplicationManager.get(snapAnimation.toIndex).appId);
311+ snapAnimation.toIndex = -1;
312+ spreadView.contentX = 0;
314+ if (spreadView.stage == 1) {
315+ spreadView.stage = 0;
316+ spreadView.contentX = 0;
304− snapAnimation.targetAppId = "";
306− snapAnimation.start();
309− function selectItem(index) {
310− tileRepeater.itemAt(index).select();
313− SequentialAnimation {
323+ Row {
314− id: snapAnimation
324+ id: spreadRow
315− property int targetContentX: 0
325+ width: implicitWidth + (spreadView.width * spreadView.positionMarker2) + (spreadView.width - spreadView.tileDistance)
316− property string targetAppId
318− UbuntuNumberAnimation {
327+ x: spreadView.stage == 0 ? 0 : spreadView.width * spreadView.positionMarker2
319− target: coverFlickable
320− properties: "contentX"
321− to: snapAnimation.targetContentX
322− duration: UbuntuAnimation.SnapDuration
326− if (snapAnimation.targetAppId) {
327− coverFlickable.passedFirstStage = false;
328− ApplicationManager.focusApplication(snapAnimation.targetAppId);
330− if (snapAnimation.targetContentX == root.width * coverFlip.progressMarker2) {
331− coverFlickable.contentX = 0;
337 Repeater {
329 Repeater {
339 model: ApplicationManager
330 model: ApplicationManager
340−
331+ delegate: TransformedSpreadDelegate {
341− Item {
332+ startAngle: 45
342− id: appItem
333+ endAngle: 10
343− height: parent.height
334+ startScale: 1.4
344− width: coverFlip.tileWidth
335+ endScale: 0.8
345−
336+ startDistance: spreadView.tileDistance
346− // This is the main progress, of the gesture, the same for every tile
337+ endDistance: units.gu(.5)
347− property real progress: coverFlip.progress
338+
348− // The progress, translated for the second stage of the animation, after the first app switch has happened
339+ z: index
349− // Additionally it speeds it up a bit, depending on the distance of the tile
340+ height: spreadView.height
350− property real translatedProgress: appItem.progress - coverFlip.progressMarker2 - (coverFlip.tileDistance * (index-1))
341+ width: {
352− // Is this tile selected by a click?
353− property bool isSelected: false
354− // We need to remember some values when app is selected to be able to animate it to the foreground
355− property real selectedXTranslation: 0
356− property real selectedTranslatedProgress: 0
357− property real selectedProgress: 0
358− property real selectedAngle: 0
359− property real selectedXScale: 0
362− appItem.selectedXTranslation = appItem.xTranslation;
363− appItem.selectedAngle = appItem.angle;
364− appItem.selectedXScale = appItem.xScale;
365− appItem.selectedTranslatedProgress = appItem.translatedProgress - coverFlip.progressMarker2;
366− appItem.selectedProgress = appItem.progress - coverFlip.progressMarker2;
367− appItem.isSelected = true;
368− switchToAppAnimation.targetContentX = coverFlip.progressMarker2 * root.width
369− switchToAppAnimation.start();
372− property int xTranslation: {
374− var minXTranslate = -index * root.width + index * units.dp(3);
375 switch (index) {
342 switch (index) {
377− if (appItem.progress < coverFlip.progressMarker2) {
344+ return spreadView.width;
378− var progress = appItem.progress
379− var progressDiff = coverFlip.progressMarker2
380− var translateDiff = -root.width * 0.25
381− // progress : progressDiff = translate : translateDiff
382− xTranslate = progress * translateDiff / progressDiff
386− if (appItem.progress < coverFlip.progressMarker2) {
346+ if (spreadView.stage == 0) {
387− var progress = appItem.progress;
347+ return spreadView.width / 2;
388− var progressDiff = coverFlip.progressMarker2;
389− var translateDiff = -root.width;
390− // progress : progressDiff = translate : translateDiff
391− xTranslate = progress * translateDiff / progressDiff;
393−// } else if (appItem.progress < coverFlip.progressMarker2) {
394−// xTranslate = -coverFlip.tileWidth
397− // Intentionally no break here...
399− if (appItem.progress > coverFlip.progressMarker2) {
350+ return spreadView.tileDistance;
400− xTranslate = xTranslateEasing.value * xTranslateEasing.period;
401− if (appItem.isSelected) {
402− var translateDiff = root.width * index + appItem.selectedXTranslation
403− var progressDiff = appItem.selectedProgress
404− var progress = progressDiff - (appItem.progress - coverFlip.progressMarker2);
405− // progress : progressDiff = translate : translateDiff
406− var newTranslate = progress * translateDiff / progressDiff;
408− xTranslate = appItem.selectedXTranslation - newTranslate;
413− if (!appItem.isSelected) {
414− xTranslate += index * units.dp(4);
419−// Behavior on xTranslation {
353+
420−// id: animatedProgressBehavior
354+ progress: {
421−// enabled: appItem.progress >= coverFlip.progressMarker1 && appItem.progress <= coverFlip.progressMarker2
422−// SmoothedAnimation {
424−// duration: UbuntuAnimation.BriskDuration;
425−// easing: UbuntuAnimation.StandardEasing
429− property real angle: {
431 switch (index) {
355 switch (index) {
433− if (appItem.progress < coverFlip.progressMarker2) {
357+ var progress = spreadView.contentX / spreadView.width;
434− var progress = appItem.progress;
358+ if (spreadView.stage == 2) {
435− var angleDiff = coverFlip.endAngle;
359+ progress -= spreadView.positionMarker1;
436− var progressDiff = coverFlip.progressMarker2;
437− // progress : progressDiff = angle : angleDiff
438− newAngle = progress * angleDiff / progressDiff;
440− var progress = appItem.progress - coverFlip.progressMarker2;
441− var angleDiff = coverFlip.endAngle;
442− var progressDiff = 1 - coverFlip.progressMarker2;
443− // progress : progressDiff = angle : angleDiff
444− newAngle = progress * angleDiff / progressDiff;
445− newAngle = Math.min(coverFlip.endAngle, newAngle);
449− if (appItem.progress < coverFlip.progressMarker2) {
364+ var progress = spreadView.contentX / spreadView.width;
450− var progress = coverFlip.progress;
365+ if (spreadView.stage != 0) {
451− var angleDiff = coverFlip.startAngle;
366+ progress -= spreadView.positionMarker2;
452− var progressDiff = coverFlip.progressMarker2;
453− // progress : progressDiff = angle : angleDiff
454− var angle = progress * angleDiff / progressDiff;
455− newAngle = coverFlip.startAngle - angle;
458− // Intentionally no break here...
460− newAngle = coverFlip.startAngle - (angleEasing.value * angleEasing.period);
461− // make sure we stop at the left screen edge
462− newAngle = Math.max(newAngle, coverFlip.endAngle);
464− if (appItem.isSelected) {
465−// var selectedAngleTranslate = selectedAngleEasing.value * selectedAngleEasing.period
466− var angleDiff = appItem.selectedAngle
467− var progressDiff = appItem.selectedProgress
468− var progress = progressDiff - (appItem.progress - coverFlip.progressMarker2);
469− // progress : progressDiff = angle : angleDiff
470− var selectedAngleTranslate = progress * angleDiff / progressDiff;
472− newAngle = appItem.selectedAngle - selectedAngleTranslate;
369+ return Math.max(0, progress);
475− return newAngle;
371+ return (spreadView.contentX - (spreadView.positionMarker2 * spreadView.width) - spreadView.tileDistance * (index - 1)) / spreadView.width;
478− property real xScale: {
374+ animatedProgress: {
479− var scale = 1;
375+ if (spreadView.stage < 2 && index == 1) {
480−
376+ if (spreadView.contentX < spreadView.width * spreadView.positionMarker1) {
481− switch (index) {
377+ return spreadView.contentX / spreadView.width
482− case 0:
378+ } else if (spreadView.stage == 0){
483− if (appItem.progress > coverFlip.progressMarker2) {
379+ return spreadView.positionMarker2
484− var scaleDiff = coverFlip.maxScale - 1;
485− var progressDiff = 1.5 - coverFlip.progressMarker2;
486− // progress : progressDiff = scale : scaleDiff
487− scale = 1 - (appItem.progress - coverFlip.progressMarker2) * scaleDiff / progressDiff;
491− if (appItem.progress < coverFlip.progressMarker2) {
492− var scaleDiff = coverFlip.maxScale - 1
493− var progressDiff = coverFlip.progressMarker2
494− // progress : progressDiff = scale : scaleDiff
495− scale = coverFlip.maxScale - (appItem.progress * scaleDiff / progressDiff);
498− // Intentionally no break
500− scale = coverFlip.maxScale - scaleEasing.value * scaleEasing.period;
501− if (appItem.isSelected) {
502− var scaleDiff = -(1 - appItem.selectedXScale)
503− var progressDiff = appItem.selectedProgress
504− var progress = progressDiff - (appItem.progress - coverFlip.progressMarker2);
505− // progress : progressDiff = angle : angleDiff
506− var selectedScaleTranslate = progress * scaleDiff / progressDiff;
508− scale = appItem.selectedXScale - selectedScaleTranslate;
511− return Math.min(coverFlip.maxScale, Math.max(coverFlip.minScale, scale));
382+ return progress;
514− EasingCurve {
385+ onClicked: {
515− id: xTranslateEasing
386+ spreadView.snapTo(index);
516− type: EasingCurve.OutQuad
517− period: index * -width
518− progress: appItem.translatedProgress
522− type: EasingCurve.InQuad
523− period: coverFlip.startAngle - coverFlip.endAngle
524− progress: appItem.translatedProgress
528− type: EasingCurve.Linear
529− period: coverFlip.maxScale - coverFlip.minScale
530− progress: appItem.translatedProgress
533− transform: [
389+ // This timer is to decouple the application reordering with the enabling of the SmoothedAnimation
534− Rotation {
390+ // If both are done in same event loop pass, the reordering would animate.
535− origin { x: 0; y: coverFlip.height / 2 }
391+ Connections {
536− axis { x: 0; y: 1; z: 0 }
392+ target: spreadView
537− angle: appItem.angle
393+ onStageChanged: {
538− },
394+ if (index == 1 && spreadView.stage == 0) {
539− Translate {
395+ enableAnimationTimer.start();
540− x: appItem.xTranslation
396+ } else {
541− },
397+ stage1Animation.enabled = false;
543− origin { x: appItem.xTranslation; y: coverFlip.height / 2 }
544− xScale: appItem.xScale
551− anchors.fill: appImage
552− anchors.margins: -units.gu(2)
553− source: "graphics/dropshadow.png"
557− anchors { left: parent.left; bottom: parent.bottom }
559− source: ApplicationManager.get(index).screenshot
562− MouseArea {
401+ Timer {
563− anchors.fill: parent
402+ id: enableAnimationTimer
564− onClicked: {
403+ interval: 1
565− appItem.select()
404+ onTriggered: {
405+ stage1Animation.enabled = true;
569− SequentialAnimation {
409+ Behavior on animatedProgress {
570− id: switchToAppAnimation
410+ id: stage1Animation
571− property int targetContentX
411+ SequentialAnimation {
572− UbuntuNumberAnimation {
412+ SmoothedAnimation {
573− target: coverFlickable;
413+ velocity: 2;
574− property: "contentX";
575− to: switchToAppAnimation.targetContentX;
576− duration: UbuntuAnimation.SnapDuration
414+ duration: UbuntuAnimation.BriskDuration;
577− }
415+ easing: UbuntuAnimation.StandardEasing
580− ApplicationManager.focusApplication(ApplicationManager.get(index).appId);
581− appItem.isSelected = false;
582− coverFlip.progress = 0;
583− coverFlickable.passedFirstStage = false;