on xenial help sign in
reimplemented right edge to be exactly like the prototype
commit4551efc2959d54d465037e35761b6e526aa2269d authorMichael Zanetti <michael.zanetti@canonical.com> dateFri 24 Jan 2014, 11:07 (13 years ago) parente0fb2d6
4 files changed+423−337
qml/Stages/PhoneStage.qml+153−321 qml/Stages/SpreadDelegate.qml+24 qml/Stages/TransformedSpreadDelegate.qml+228 tests/mocks/Unity/Application/ApplicationManager.cpp+18−16
qml/Stages/PhoneStage.qml+153−321
@@ -188,25 +188,34 @@ Item {
188188 }
189189
190190 EdgeDragArea {
191− id: coverFlipDragArea
191+ id: spreadDragArea
192192 direction: Direction.Leftwards
193193
194194 //enabled: root.available
195195 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
196196 width: root.dragAreaWidth
197197
198+ property bool attachedToView: true
199+
198200 onTouchXChanged: {
199201 if (!dragging && !priv.waitingForScreenshot) {
200202 priv.requestNewScreenshot();
203+ spreadView.stage = 0;
204+ attachedToView = true;
205+ }
206+ if (dragging && !priv.waitingForScreenshot && attachedToView) {
207+ spreadView.contentX = -touchX
201208 }
202− if (dragging && !priv.waitingForScreenshot) {
203− coverFlickable.contentX = -touchX
209+ if (attachedToView && spreadView.contentX >= spreadView.width * spreadView.positionMarker3) {
210+ attachedToView = false;
211+ spreadView.snap();
204212 }
205213 }
206214
207215 onDraggingChanged: {
208− if (!dragging) {
209− coverFlip.snap();
216+ print("Dragging changed", dragging)
217+ if (!dragging && attachedToView) {
218+ spreadView.snap();
210219 }
211220 }
212221 }
@@ -215,372 +224,195 @@ Item {
215224 id: coverFlipBackground
216225 anchors.fill: parent
217226 color: "black"
218− visible: coverFlip.visible
227+ visible: spreadView.visible
219228 }
220229
221230 InputFilterArea {
222231 anchors.fill: root
223− blockInput: coverFlip.visible
232+ blockInput: spreadView.visible
224233 }
225234
226235 Flickable {
227− id: coverFlickable
228− anchors.fill: root
229− contentHeight: height
230− contentWidth: width * 2 + ((ApplicationManager.count - 2) * root.width * coverFlip.tileDistance * 1.5)
231− flickableDirection: Qt.Horizontal
232− enabled: coverFlip.visible
236+ id: spreadView
237+ anchors.fill: parent
238+ visible: contentX > 0
239+ contentWidth: spreadRow.width
233240
234− property bool passedFirstStage: false
241+ property int tileDistance: width / 4
242+
243+ property real positionMarker1: 0.25
244+ property real positionMarker2: 0.5
245+ property real positionMarker3: 0.7
246+ property real positionMarker4: 1.25
247+
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
235253
236254 onContentXChanged: {
237− if (coverFlickable.passedFirstStage && contentX < width * coverFlip.progressMarker2) {
238− contentX = width * coverFlip.progressMarker2;
239− return;
240− }
241− var progress = contentX / width
242− if (progress > coverFlip.progressMarker2) {
243− coverFlickable.passedFirstStage = true;
255+ switch (stage) {
256+ case 0:
257+ if (contentX > width * positionMarker2) {
258+ print("switching to app 1 .")
259+ ApplicationManager.focusApplication(ApplicationManager.get(1).appId)
260+ stage = 1;
261+ }
262+ break;
263+ case 1:
264+ if (contentX < width * positionMarker2) {
265+ print("switching to app 1 -")
266+ ApplicationManager.focusApplication(ApplicationManager.get(1).appId)
267+ stage = 0;
268+ } else if (contentX >= width * positionMarker4) {
269+ stage = 2;
270+ }
244271 }
245− coverFlip.progress = progress;
246272 }
247273
248− Row {
249− id: coverFlip
250− height: parent.height
251− // The MouseAreas on the AppImages need to be children of the flickable in order to make focus stealing
252− // for flicking vs. clicking work correctly.
253− // However, for the animation calculations to become easier we don't want the row to move so we don't
254− // always have to take contentX into account. So lets just compensate the flickable's movement here.
255− x: coverFlickable.contentX
256− visible: progress > 0
257−
258− property real progress: 0
259− property real startAngle: 45
260− property int endAngle: 10
261−
262− property real maxScale: 1.4
263− property real minScale: .6
264−
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
269−
270− property real tileDistance: 0.1
271−
272− property bool animatingBack: false
273−
274− property real tileWidth: root.width
275−
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)
283− }
284− }
285− oldProgress = progress;
274+ function snap() {
275+ if (contentX < positionMarker1 * width) {
276+ snapAnimation.targetContentX = 0;
277+ snapAnimation.start();
278+ } else if (contentX < positionMarker2 * width) {
279+ snapTo(1)
280+ } else if (contentX < positionMarker3 * width) {
281+ snapAnimation.targetContentX = width * positionMarker2 + 1;
282+ snapAnimation.start();
283+ } else if (stage < 2){
284+ snapAnimation.targetContentX = width * positionMarker4;
285+ snapAnimation.start();
286286 }
287+ }
288+ function snapTo(index) {
289+ snapAnimation.targetContentX = width * positionMarker2;
290+ snapAnimation.toIndex = index;
291+ snapAnimation.start();
292+ }
287293
288− function snap() {
289− if (coverFlip.progress < coverFlip.progressMarker1) {
290− snapAnimation.targetContentX = 0
291− snapAnimation.targetAppId = ApplicationManager.get(0).appId;
292− } else if (coverFlip.progress < coverFlip.progressMarker2) {
293− snapAnimation.targetContentX = root.width * coverFlip.progressMarker2
294− snapAnimation.targetAppId = ApplicationManager.get(1).appId;
295− } else if (coverFlip.progress < 0.6) {
296− snapAnimation.targetContentX = root.width * coverFlip.progressMarker2
297− snapAnimation.targetAppId = ApplicationManager.get(0).appId;
298− } else {
299− if (ApplicationManager.count == 3) {
300− snapAnimation.targetContentX = root.width * 1.1;
301− } else {
302− snapAnimation.targetContentX = root.width * 1.25;
294+ SequentialAnimation {
295+ id: snapAnimation
296+ property int targetContentX: 0
297+ property int toIndex: -1
298+
299+ UbuntuNumberAnimation {
300+ target: spreadView
301+ property: "contentX"
302+ to: snapAnimation.targetContentX
303+ duration: UbuntuAnimation.SnapDuration
304+ }
305+ ScriptAction {
306+ script: {
307+ print("animation finished: stage", spreadView.stage, "contentX", spreadView.contentX, "focused app", ApplicationManager.get(0).appId, ApplicationManager.get(1).appId)
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;
313+ }
314+ if (spreadView.stage == 1) {
315+ spreadView.stage = 0;
316+ spreadView.contentX = 0;
303317 }
304− snapAnimation.targetAppId = "";
305318 }
306− snapAnimation.start();
307319 }
320+ }
308321
309− function selectItem(index) {
310− tileRepeater.itemAt(index).select();
311− }
312322
313− SequentialAnimation {
314− id: snapAnimation
315− property int targetContentX: 0
316− property string targetAppId
323+ Row {
324+ id: spreadRow
325+ width: implicitWidth + (spreadView.width * spreadView.positionMarker2) + (spreadView.width - spreadView.tileDistance)
317326
318− UbuntuNumberAnimation {
319− target: coverFlickable
320− properties: "contentX"
321− to: snapAnimation.targetContentX
322− duration: UbuntuAnimation.SnapDuration
323− }
324− ScriptAction {
325− script: {
326− if (snapAnimation.targetAppId) {
327− coverFlickable.passedFirstStage = false;
328− ApplicationManager.focusApplication(snapAnimation.targetAppId);
329− }
330− if (snapAnimation.targetContentX == root.width * coverFlip.progressMarker2) {
331− coverFlickable.contentX = 0;
332− }
333− }
334− }
335− }
327+ x: spreadView.stage == 0 ? 0 : spreadView.width * spreadView.positionMarker2
336328
337329 Repeater {
338− id: tileRepeater
339330 model: ApplicationManager
340−
341− Item {
342− id: appItem
343− height: parent.height
344− width: coverFlip.tileWidth
345−
346− // This is the main progress, of the gesture, the same for every tile
347− property real progress: coverFlip.progress
348− // The progress, translated for the second stage of the animation, after the first app switch has happened
349− // Additionally it speeds it up a bit, depending on the distance of the tile
350− property real translatedProgress: appItem.progress - coverFlip.progressMarker2 - (coverFlip.tileDistance * (index-1))
351−
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
360−
361− function select() {
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();
370− }
371−
372− property int xTranslation: {
373− var xTranslate = 0;
374− var minXTranslate = -index * root.width + index * units.dp(3);
331+ delegate: TransformedSpreadDelegate {
332+ startAngle: 45
333+ endAngle: 10
334+ startScale: 1.4
335+ endScale: 0.8
336+ startDistance: spreadView.tileDistance
337+ endDistance: units.gu(.5)
338+
339+ z: index
340+ height: spreadView.height
341+ width: {
375342 switch (index) {
376343 case 0:
377− if (appItem.progress < coverFlip.progressMarker2) {
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
383− }
384− break;
344+ return spreadView.width;
385345 case 1:
386− if (appItem.progress < coverFlip.progressMarker2) {
387− var progress = appItem.progress;
388− var progressDiff = coverFlip.progressMarker2;
389− var translateDiff = -root.width;
390− // progress : progressDiff = translate : translateDiff
391− xTranslate = progress * translateDiff / progressDiff;
392− break;
393−// } else if (appItem.progress < coverFlip.progressMarker2) {
394−// xTranslate = -coverFlip.tileWidth
346+ if (spreadView.stage == 0) {
347+ return spreadView.width / 2;
395348 }
396−
397− // Intentionally no break here...
398349 default:
399− if (appItem.progress > coverFlip.progressMarker2) {
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;
407−
408− xTranslate = appItem.selectedXTranslation - newTranslate;
409− }
410− break;
411− }
412− }
413− if (!appItem.isSelected) {
414− xTranslate += index * units.dp(4);
350+ return spreadView.tileDistance;
415351 }
416−
417− return xTranslate;
418352 }
419−// Behavior on xTranslation {
420−// id: animatedProgressBehavior
421−// enabled: appItem.progress >= coverFlip.progressMarker1 && appItem.progress <= coverFlip.progressMarker2
422−// SmoothedAnimation {
423−// velocity: 0.5;
424−// duration: UbuntuAnimation.BriskDuration;
425−// easing: UbuntuAnimation.StandardEasing
426−// }
427−// }
428−
429− property real angle: {
430− var newAngle = 0;
353+
354+ progress: {
431355 switch (index) {
432356 case 0:
433− if (appItem.progress < coverFlip.progressMarker2) {
434− var progress = appItem.progress;
435− var angleDiff = coverFlip.endAngle;
436− var progressDiff = coverFlip.progressMarker2;
437− // progress : progressDiff = angle : angleDiff
438− newAngle = progress * angleDiff / progressDiff;
439− } else {
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);
357+ var progress = spreadView.contentX / spreadView.width;
358+ if (spreadView.stage == 2) {
359+ progress -= spreadView.positionMarker1;
446360 }
447− break;
361+
362+ return progress;
448363 case 1:
449− if (appItem.progress < coverFlip.progressMarker2) {
450− var progress = coverFlip.progress;
451− var angleDiff = coverFlip.startAngle;
452− var progressDiff = coverFlip.progressMarker2;
453− // progress : progressDiff = angle : angleDiff
454− var angle = progress * angleDiff / progressDiff;
455− newAngle = coverFlip.startAngle - angle;
456− break;
457− }
458− // Intentionally no break here...
459− default:
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);
463−
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;
471−
472− newAngle = appItem.selectedAngle - selectedAngleTranslate;
364+ var progress = spreadView.contentX / spreadView.width;
365+ if (spreadView.stage != 0) {
366+ progress -= spreadView.positionMarker2;
473367 }
368+
369+ return Math.max(0, progress);
474370 }
475− return newAngle;
371+ return (spreadView.contentX - (spreadView.positionMarker2 * spreadView.width) - spreadView.tileDistance * (index - 1)) / spreadView.width;
476372 }
477373
478− property real xScale: {
479− var scale = 1;
480−
481− switch (index) {
482− case 0:
483− if (appItem.progress > coverFlip.progressMarker2) {
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;
488− }
489− break;
490− case 1:
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);
496− break;
497− }
498− // Intentionally no break
499− default:
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;
507−
508− scale = appItem.selectedXScale - selectedScaleTranslate;
374+ animatedProgress: {
375+ if (spreadView.stage < 2 && index == 1) {
376+ if (spreadView.contentX < spreadView.width * spreadView.positionMarker1) {
377+ return spreadView.contentX / spreadView.width
378+ } else if (spreadView.stage == 0){
379+ return spreadView.positionMarker2
509380 }
510381 }
511− return Math.min(coverFlip.maxScale, Math.max(coverFlip.minScale, scale));
382+ return progress;
512383 }
513384
514− EasingCurve {
515− id: xTranslateEasing
516− type: EasingCurve.OutQuad
517− period: index * -width
518− progress: appItem.translatedProgress
519− }
520− EasingCurve {
521− id: angleEasing
522− type: EasingCurve.InQuad
523− period: coverFlip.startAngle - coverFlip.endAngle
524− progress: appItem.translatedProgress
525− }
526− EasingCurve {
527− id: scaleEasing
528− type: EasingCurve.Linear
529− period: coverFlip.maxScale - coverFlip.minScale
530− progress: appItem.translatedProgress
385+ onClicked: {
386+ spreadView.snapTo(index);
531387 }
532388
533− transform: [
534− Rotation {
535− origin { x: 0; y: coverFlip.height / 2 }
536− axis { x: 0; y: 1; z: 0 }
537− angle: appItem.angle
538− },
539− Translate {
540− x: appItem.xTranslation
541− },
542− Scale {
543− origin { x: appItem.xTranslation; y: coverFlip.height / 2 }
544− xScale: appItem.xScale
545− yScale: xScale
389+ // This timer is to decouple the application reordering with the enabling of the SmoothedAnimation
390+ // If both are done in same event loop pass, the reordering would animate.
391+ Connections {
392+ target: spreadView
393+ onStageChanged: {
394+ if (index == 1 && spreadView.stage == 0) {
395+ enableAnimationTimer.start();
396+ } else {
397+ stage1Animation.enabled = false;
398+ }
546399 }
547− ]
548−
549− Image {
550− id: dropShadow
551− anchors.fill: appImage
552− anchors.margins: -units.gu(2)
553− source: "graphics/dropshadow.png"
554− }
555− Image {
556− id: appImage
557− anchors { left: parent.left; bottom: parent.bottom }
558− width: root.width
559− source: ApplicationManager.get(index).screenshot
560− scale: 1
561400 }
562− MouseArea {
563− anchors.fill: parent
564− onClicked: {
565− appItem.select()
401+ Timer {
402+ id: enableAnimationTimer
403+ interval: 1
404+ onTriggered: {
405+ stage1Animation.enabled = true;
566406 }
567407 }
568408
569− SequentialAnimation {
570− id: switchToAppAnimation
571− property int targetContentX
572− UbuntuNumberAnimation {
573− target: coverFlickable;
574− property: "contentX";
575− to: switchToAppAnimation.targetContentX;
576− duration: UbuntuAnimation.SnapDuration
577− }
578− ScriptAction {
579− script: {
580− ApplicationManager.focusApplication(ApplicationManager.get(index).appId);
581− appItem.isSelected = false;
582− coverFlip.progress = 0;
583− coverFlickable.passedFirstStage = false;
409+ Behavior on animatedProgress {
410+ id: stage1Animation
411+ SequentialAnimation {
412+ SmoothedAnimation {
413+ velocity: 2;
414+ duration: UbuntuAnimation.BriskDuration;
415+ easing: UbuntuAnimation.StandardEasing
584416 }
585417 }
586418 }
qml/Stages/SpreadDelegate.qmlnew file+24
@@ -0,0 +1,24 @@
1+import QtQuick 2.0
2+
3+Item {
4+ id: root
5+
6+ signal clicked()
7+
8+ Image {
9+ id: dropShadow
10+ anchors.fill: appImage
11+ anchors.margins: -units.gu(2)
12+ source: "graphics/dropshadow.png"
13+ }
14+ Image {
15+ id: appImage
16+ anchors { left: parent.left; bottom: parent.bottom }
17+ source: model.screenshot
18+ scale: 1
19+ }
20+ MouseArea {
21+ anchors.fill: appImage
22+ onClicked: root.clicked()
23+ }
24+}
qml/Stages/TransformedSpreadDelegate.qmlnew file+228
@@ -0,0 +1,228 @@
1+import QtQuick 2.0
2+import Utils 0.1
3+import Ubuntu.Components 0.1
4+
5+SpreadDelegate {
6+ id: root
7+
8+ property real progress: 0
9+// onProgressChanged: if (index == 0) print("tile", index, "progress changed:", progress)
10+ property real animatedProgress: 0
11+
12+ property real startAngle: 0
13+ property real endAngle: 0
14+
15+ property real startScale: 1
16+ property real endScale: 1
17+
18+ property real startDistance: units.gu(5)
19+ property real endDistance: units.gu(.5)
20+
21+ onClicked: {
22+ priv.selectedProgress = root.progress
23+ priv.selectedXTranslate = priv.xTranslate;
24+ priv.selectedAngle = priv.angle;
25+ priv.selectedScale = priv.scale;
26+ priv.isSelected = true;
27+ }
28+
29+ Connections {
30+ target: spreadView
31+ onStageChanged: {
32+ if (stage == 0) {
33+ priv.isSelected = false;
34+ }
35+ }
36+ }
37+
38+ QtObject {
39+ id: priv
40+ property bool isSelected: false
41+ property real selectedProgress
42+ property real selectedXTranslate
43+ property real selectedAngle
44+ property real selectedScale
45+
46+ property real negativeProgress: (index - 1) * spreadView.positionMarker1
47+
48+
49+ property real xTranslate: {
50+ var translation = 0;
51+ switch (index) {
52+ case 0:
53+ if (spreadView.stage == 0) {
54+ // Need to compensate the flickable movement (fixing first tile at the left edge)
55+ var compensation = root.progress * spreadView.width
56+
57+ // Calculating the small movement of the first tile
58+ var progress = root.progress
59+ var progressDiff = spreadView.positionMarker2
60+ var translateDiff = -spreadView.width * 0.25
61+ // progress : progressDiff = translate : translateDiff
62+ var movement = progress * translateDiff / progressDiff
63+
64+ translation = compensation + movement;
65+ } else if (spreadView.stage == 1){
66+ // Need to compensate the flickable movement (fixing first tile at the left edge)
67+ var compensation = spreadView.contentX - spreadRow.x
68+ translation = compensation;
69+ } else { // Stage 2
70+ // Move the first tile a bit to the left to be aligned with the others
71+ translation += spreadView.contentX - spreadRow.x + spreadView.width
72+ // apply the same animation as with the rest
73+ translation += -xTranslateEasing.value * spreadView.width
74+ }
75+ break;
76+ case 1:
77+ if (spreadView.stage == 0) {
78+ // Need to compensate the flickable movement (fix it to the right edge)
79+ var compensation = root.progress * spreadView.width
80+
81+ // Calculate movemoent for the first app coming from the right
82+ var progress = root.animatedProgress
83+ var progressDiff = spreadView.positionMarker2
84+ var translateDiff = -spreadView.width
85+ // progress : progressDiff = translate : translateDiff
86+ var movement = progress * translateDiff / progressDiff
87+
88+ translation = compensation + movement;
89+ break;
90+ }
91+ default:
92+ // Fix it at the right edge...
93+ translation += spreadView.contentX - spreadRow.x - ((index - 1) * root.startDistance)
94+ // ...and use our easing to move them to the left. Stop a bit earlier for each tile
95+ translation += -xTranslateEasing.value * spreadView.width + (index * root.endDistance)
96+ if (isSelected) {
97+ var progressDiff = negativeProgress + selectedProgress
98+ var progress = progressDiff - (root.progress + negativeProgress);
99+ var translateDiff = spreadView.width - (selectedXTranslate - translation)
100+
101+ // Fix it where it was when selected
102+ translation = selectedXTranslate - progress * spreadView.width
103+
104+ // Distance to left edge
105+ var targetTranslate = spreadView.contentX - spreadView.width - spreadRow.x - ((index - 1) * root.startDistance)
106+
107+ // The distance to animate is from the selection position to the left edge
108+ var translateDiff = targetTranslate - translation
109+
110+ translation += progress * translateDiff / progressDiff;
111+ }
112+ }
113+ return translation;
114+ }
115+ property real angle: {
116+ var newAngle = 0;
117+ switch (index) {
118+ case 0:
119+ if (spreadView.stage == 0) {
120+ var progress = root.progress;
121+ var progressDiff = spreadView.positionMarker2;
122+ var angleDiff = root.endAngle;
123+ // progress : progressDiff = angle : angleDiff
124+ newAngle = progress * angleDiff / progressDiff;
125+ break;
126+ } else if (spreadView.stage == 1){
127+ var progress = root.progress - spreadView.positionMarker2;
128+ var angleDiff = root.endAngle;
129+ var progressDiff = 1 - spreadView.positionMarker2;
130+ newAngle = progress * angleDiff / progressDiff;
131+ newAngle = Math.min(root.endAngle, newAngle);
132+ break;
133+ }
134+ case 1:
135+ if (spreadView.stage == 0) {
136+ var progress = root.animatedProgress;
137+ var progressDiff = spreadView.positionMarker2;
138+ var angleDiff = root.startAngle
139+
140+ // progress : progressDiff = angle : angleDiff
141+ var angleTranslate = progress * angleDiff / progressDiff;
142+
143+ newAngle = root.startAngle - angleTranslate;
144+ break;
145+ }
146+ default:
147+ newAngle = root.startAngle - xTranslateEasing.value * (root.startAngle - root.endAngle)
148+ if (isSelected) {
149+ var progressDiff = negativeProgress + selectedProgress
150+ var progress = progressDiff - (root.progress + negativeProgress);
151+ var angleDiff = -selectedAngle;
152+ var angleTranslate = progress * angleDiff / progressDiff;
153+
154+ newAngle = selectedAngle + angleTranslate;
155+
156+ }
157+ }
158+
159+ return newAngle;
160+ }
161+ property real scale: {
162+ var newScale = 1;
163+ switch (index) {
164+ case 0:
165+ if (spreadView.stage == 0) {
166+ newScale = 1;
167+ } else {
168+ var progress = root.progress - spreadView.positionMarker2;
169+ var scaleDiff = 1 - root.endScale;
170+ var progressDiff = 1 - spreadView.positionMarker2;
171+// // progress : progressDiff = angle : angleDiff
172+ newScale = 1 - (progress * scaleDiff / progressDiff);
173+ newScale = Math.max(root.endScale, newScale);
174+ }
175+
176+ break;
177+ case 1:
178+ if (spreadView.stage == 0) {
179+ var progress = root.animatedProgress;
180+ var progressDiff = spreadView.positionMarker2;
181+ var scaleDiff = root.startScale - 1
182+
183+ // progress : progressDiff = angle : angleDiff
184+ var scaleTranslate = progress * scaleDiff / progressDiff;
185+
186+ newScale = root.startScale - scaleTranslate;
187+ break;
188+ }
189+ default:
190+ newScale = root.startScale - xTranslateEasing.value * (root.startScale - root.endScale)
191+ if (isSelected) {
192+ var progressDiff = negativeProgress + selectedProgress
193+ var progress = progressDiff - (root.progress + negativeProgress);
194+ var scaleDiff = 1 - selectedScale
195+
196+ // progress : progressDiff = angle : angleDiff
197+ var scaleTranslate = progress * scaleDiff / progressDiff;
198+
199+ newScale = selectedScale + scaleTranslate;
200+ }
201+ }
202+ return newScale;
203+ }
204+ }
205+
206+ transform: [
207+ Rotation {
208+ origin { x: 0; y: spreadView.height / 2 }
209+ axis { x: 0; y: 1; z: 0 }
210+ angle: priv.angle
211+ },
212+ Scale {
213+ origin { x: 0; y: spreadView.height / 2 }
214+ xScale: priv.scale
215+ yScale: xScale
216+ },
217+ Translate {
218+ x: priv.xTranslate
219+ }
220+ ]
221+
222+ EasingCurve {
223+ id: xTranslateEasing
224+ type: EasingCurve.OutQuad
225+ period: 1 - spreadView.positionMarker2
226+ progress: root.animatedProgress
227+ }
228+}
tests/mocks/Unity/Application/ApplicationManager.cpp+18−16
@@ -80,9 +80,7 @@ ApplicationInfo *ApplicationManager::get(int row) const {
8080}
8181
8282ApplicationInfo *ApplicationManager::findApplication(const QString &appId) const {
83− qDebug() << "findapp called";
8483 for (ApplicationInfo *app : m_runningApplications) {
85− qDebug() << "searching app"<< app->appId();
8684 if (app->appId() == appId) {
8785 return app;
8886 }
@@ -91,10 +89,10 @@ ApplicationInfo *ApplicationManager::findApplication(const QString &appId) const
9189}
9290
9391void ApplicationManager::add(ApplicationInfo *application) {
94− qDebug() << "add() " << application->appId();
9592 if (!application) {
9693 return;
9794 }
95+ qDebug() << "add() " << application->appId();
9896
9997 beginInsertRows(QModelIndex(), m_runningApplications.size(), m_runningApplications.size());
10098 m_runningApplications.append(application);
@@ -341,7 +339,7 @@ void ApplicationManager::buildListOfAvailableApplications()
341339 application->setName("Phone");
342340 application->setIcon(QUrl("phone"));
343341 application->setStage(ApplicationInfo::SideStage);
344− application->setScreenshot(QString("image://application/%1/123456789").arg(application->appId()));
342+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
345343 generateQmlStrings(application);
346344 m_availableApplications.append(application);
347345
@@ -350,7 +348,7 @@ void ApplicationManager::buildListOfAvailableApplications()
350348 application->setName("Camera");
351349 application->setIcon(QUrl("camera"));
352350 application->setFullscreen(true);
353− application->setScreenshot(QUrl("image://application/phone/123456789"));
351+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
354352 generateQmlStrings(application);
355353 m_availableApplications.append(application);
356354
@@ -358,7 +356,7 @@ void ApplicationManager::buildListOfAvailableApplications()
358356 application->setAppId("gallery-app");
359357 application->setName("Gallery");
360358 application->setIcon(QUrl("gallery"));
361− application->setScreenshot(QUrl("image://application/phone/123456789"));
359+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
362360 generateQmlStrings(application);
363361 m_availableApplications.append(application);
364362
@@ -367,7 +365,7 @@ void ApplicationManager::buildListOfAvailableApplications()
367365 application->setName("Facebook");
368366 application->setIcon(QUrl("facebook"));
369367 application->setStage(ApplicationInfo::SideStage);
370− application->setScreenshot(QUrl("image://application/phone/123456789"));
368+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
371369 generateQmlStrings(application);
372370 m_availableApplications.append(application);
373371
@@ -375,7 +373,7 @@ void ApplicationManager::buildListOfAvailableApplications()
375373 application->setAppId("webbrowser-app");
376374 application->setName("Browser");
377375 application->setIcon(QUrl("browser"));
378− application->setScreenshot(QUrl("image://application/phone/123456789"));
376+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
379377 generateQmlStrings(application);
380378 m_availableApplications.append(application);
381379
@@ -384,7 +382,7 @@ void ApplicationManager::buildListOfAvailableApplications()
384382 application->setName("Twitter");
385383 application->setIcon(QUrl("twitter"));
386384 application->setStage(ApplicationInfo::SideStage);
387− application->setScreenshot(QUrl("image://application/phone/123456789"));
385+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
388386 generateQmlStrings(application);
389387 m_availableApplications.append(application);
390388
@@ -392,7 +390,7 @@ void ApplicationManager::buildListOfAvailableApplications()
392390 application->setAppId("gmail-webapp");
393391 application->setName("GMail");
394392 application->setIcon(QUrl("gmail"));
395− application->setScreenshot(QUrl("image://application/phone/123456789"));
393+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
396394 m_availableApplications.append(application);
397395
398396 application = new ApplicationInfo(this);
@@ -400,7 +398,7 @@ void ApplicationManager::buildListOfAvailableApplications()
400398 application->setName("Weather");
401399 application->setIcon(QUrl("weather"));
402400 application->setStage(ApplicationInfo::SideStage);
403− application->setScreenshot(QUrl("image://application/phone/123456789"));
401+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
404402 generateQmlStrings(application);
405403 m_availableApplications.append(application);
406404
@@ -409,7 +407,7 @@ void ApplicationManager::buildListOfAvailableApplications()
409407 application->setName("Notepad");
410408 application->setIcon(QUrl("notepad"));
411409 application->setStage(ApplicationInfo::SideStage);
412− application->setScreenshot(QUrl("image://application/phone/123456789"));
410+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
413411 m_availableApplications.append(application);
414412
415413 application = new ApplicationInfo(this);
@@ -417,7 +415,7 @@ void ApplicationManager::buildListOfAvailableApplications()
417415 application->setName("Calendar");
418416 application->setIcon(QUrl("calendar"));
419417 application->setStage(ApplicationInfo::SideStage);
420− application->setScreenshot(QUrl("image://application/phone/123456789"));
418+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
421419 m_availableApplications.append(application);
422420
423421 application = new ApplicationInfo(this);
@@ -425,21 +423,21 @@ void ApplicationManager::buildListOfAvailableApplications()
425423 application->setName("Media Player");
426424 application->setIcon(QUrl("mediaplayer-app"));
427425 application->setFullscreen(true);
428− application->setScreenshot(QUrl("image://application/phone/123456789"));
426+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
429427 m_availableApplications.append(application);
430428
431429 application = new ApplicationInfo(this);
432430 application->setAppId("evernote");
433431 application->setName("Evernote");
434432 application->setIcon(QUrl("evernote"));
435− application->setScreenshot(QUrl("image://application/phone/123456789"));
433+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
436434 m_availableApplications.append(application);
437435
438436 application = new ApplicationInfo(this);
439437 application->setAppId("map");
440438 application->setName("Map");
441439 application->setIcon(QUrl("map"));
442− application->setScreenshot(QUrl("image://application/phone/123456789"));
440+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
443441 generateQmlStrings(application);
444442 m_availableApplications.append(application);
445443
@@ -447,24 +445,28 @@ void ApplicationManager::buildListOfAvailableApplications()
447445 application->setAppId("pinterest");
448446 application->setName("Pinterest");
449447 application->setIcon(QUrl("pinterest"));
448+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
450449 m_availableApplications.append(application);
451450
452451 application = new ApplicationInfo(this);
453452 application->setAppId("soundcloud");
454453 application->setName("SoundCloud");
455454 application->setIcon(QUrl("soundcloud"));
455+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
456456 m_availableApplications.append(application);
457457
458458 application = new ApplicationInfo(this);
459459 application->setAppId("wikipedia");
460460 application->setName("Wikipedia");
461461 application->setIcon(QUrl("wikipedia"));
462+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
462463 m_availableApplications.append(application);
463464
464465 application = new ApplicationInfo(this);
465466 application->setAppId("youtube");
466467 application->setName("YouTube");
467468 application->setIcon(QUrl("youtube"));
469+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
468470 m_availableApplications.append(application);
469471}
470472