on xenial help sign in
reimplemented right edge to be exactly like the prototype
4551efcMichael Zanetti
qml/Stages/PhoneStage.qml+153−321
@@ -188,25 +188,34 @@ Item {
188 }
188 }
189
189
190 EdgeDragArea {
190 EdgeDragArea {
191− id: coverFlipDragArea
191+ id: spreadDragArea
192 direction: Direction.Leftwards
192 direction: Direction.Leftwards
193
193
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
197
197
198+ property bool attachedToView: true
199+
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;
205+ }
206+ if (dragging && !priv.waitingForScreenshot && attachedToView) {
207+ spreadView.contentX = -touchX
201 }
208 }
202− if (dragging && !priv.waitingForScreenshot) {
209+ if (attachedToView && spreadView.contentX >= spreadView.width * spreadView.positionMarker3) {
203− coverFlickable.contentX = -touchX
210+ attachedToView = false;
211+ spreadView.snap();
204 }
212 }
205 }
213 }
206
214
207 onDraggingChanged: {
215 onDraggingChanged: {
216+ print("Dragging changed", dragging)
208− if (!dragging) {
217+ if (!dragging && attachedToView) {
209− coverFlip.snap();
218+ spreadView.snap();
210 }
219 }
211 }
220 }
212 }
221 }
@@ -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
219 }
228 }
220
229
221 InputFilterArea {
230 InputFilterArea {
222 anchors.fill: root
231 anchors.fill: root
223− blockInput: coverFlip.visible
232+ blockInput: spreadView.visible
224 }
233 }
225
234
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
233
240
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
235
253
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+ }
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+ }
244 }
271 }
245− coverFlip.progress = progress;
246 }
272 }
247
273
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
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;
286 }
286 }
287+ }
288+ function snapTo(index) {
289+ snapAnimation.targetContentX = width * positionMarker2;
290+ snapAnimation.toIndex = index;
291+ snapAnimation.start();
292+ }
287
293
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
298− } else {
304+ }
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;
313+ }
314+ if (spreadView.stage == 1) {
315+ spreadView.stage = 0;
316+ spreadView.contentX = 0;
303 }
317 }
304− snapAnimation.targetAppId = "";
305 }
318 }
306− snapAnimation.start();
307 }
319 }
320+ }
308
321
309− function selectItem(index) {
310− tileRepeater.itemAt(index).select();
311− }
312
322
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
317
326
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
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− }
336
328
337 Repeater {
329 Repeater {
338− id: tileRepeater
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: {
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);
375 switch (index) {
342 switch (index) {
376 case 0:
343 case 0:
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
383− }
384− break;
385 case 1:
345 case 1:
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;
392− break;
393−// } else if (appItem.progress < coverFlip.progressMarker2) {
394−// xTranslate = -coverFlip.tileWidth
395 }
348 }
396−
397− // Intentionally no break here...
398 default:
349 default:
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;
407−
408− xTranslate = appItem.selectedXTranslation - newTranslate;
409− }
410− break;
411− }
412− }
413− if (!appItem.isSelected) {
414− xTranslate += index * units.dp(4);
415 }
351 }
416−
417− return xTranslate;
418 }
352 }
419−// Behavior on xTranslation {
353+
420−// id: animatedProgressBehavior
354+ progress: {
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;
431 switch (index) {
355 switch (index) {
432 case 0:
356 case 0:
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;
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);
446 }
360 }
447− break;
361+
362+ return progress;
448 case 1:
363 case 1:
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;
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;
473 }
367 }
368+
369+ return Math.max(0, progress);
474 }
370 }
475− return newAngle;
371+ return (spreadView.contentX - (spreadView.positionMarker2 * spreadView.width) - spreadView.tileDistance * (index - 1)) / spreadView.width;
476 }
372 }
477
373
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;
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;
509 }
380 }
510 }
381 }
511− return Math.min(coverFlip.maxScale, Math.max(coverFlip.minScale, scale));
382+ return progress;
512 }
383 }
513
384
514− EasingCurve {
385+ onClicked: {
515− id: xTranslateEasing
386+ spreadView.snapTo(index);
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
531 }
387 }
532
388
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;
542− Scale {
398+ }
543− origin { x: appItem.xTranslation; y: coverFlip.height / 2 }
544− xScale: appItem.xScale
545− yScale: xScale
546 }
399 }
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
561 }
400 }
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;
566 }
406 }
567 }
407 }
568
408
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
578− ScriptAction {
579− script: {
580− ApplicationManager.focusApplication(ApplicationManager.get(index).appId);
581− appItem.isSelected = false;
582− coverFlip.progress = 0;
583− coverFlickable.passedFirstStage = false;
584 }
416 }
585 }
417 }
586 }
418 }
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 {
80}
80}
81
81
82ApplicationInfo *ApplicationManager::findApplication(const QString &appId) const {
82ApplicationInfo *ApplicationManager::findApplication(const QString &appId) const {
83− qDebug() << "findapp called";
84 for (ApplicationInfo *app : m_runningApplications) {
83 for (ApplicationInfo *app : m_runningApplications) {
85− qDebug() << "searching app"<< app->appId();
86 if (app->appId() == appId) {
84 if (app->appId() == appId) {
87 return app;
85 return app;
88 }
86 }
@@ -91,10 +89,10 @@ ApplicationInfo *ApplicationManager::findApplication(const QString &appId) const
91}
89}
92
90
93void ApplicationManager::add(ApplicationInfo *application) {
91void ApplicationManager::add(ApplicationInfo *application) {
94− qDebug() << "add() " << application->appId();
95 if (!application) {
92 if (!application) {
96 return;
93 return;
97 }
94 }
95+ qDebug() << "add() " << application->appId();
98
96
99 beginInsertRows(QModelIndex(), m_runningApplications.size(), m_runningApplications.size());
97 beginInsertRows(QModelIndex(), m_runningApplications.size(), m_runningApplications.size());
100 m_runningApplications.append(application);
98 m_runningApplications.append(application);
@@ -341,7 +339,7 @@ void ApplicationManager::buildListOfAvailableApplications()
341 application->setName("Phone");
339 application->setName("Phone");
342 application->setIcon(QUrl("phone"));
340 application->setIcon(QUrl("phone"));
343 application->setStage(ApplicationInfo::SideStage);
341 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()));
345 generateQmlStrings(application);
343 generateQmlStrings(application);
346 m_availableApplications.append(application);
344 m_availableApplications.append(application);
347
345
@@ -350,7 +348,7 @@ void ApplicationManager::buildListOfAvailableApplications()
350 application->setName("Camera");
348 application->setName("Camera");
351 application->setIcon(QUrl("camera"));
349 application->setIcon(QUrl("camera"));
352 application->setFullscreen(true);
350 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()));
354 generateQmlStrings(application);
352 generateQmlStrings(application);
355 m_availableApplications.append(application);
353 m_availableApplications.append(application);
356
354
@@ -358,7 +356,7 @@ void ApplicationManager::buildListOfAvailableApplications()
358 application->setAppId("gallery-app");
356 application->setAppId("gallery-app");
359 application->setName("Gallery");
357 application->setName("Gallery");
360 application->setIcon(QUrl("gallery"));
358 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()));
362 generateQmlStrings(application);
360 generateQmlStrings(application);
363 m_availableApplications.append(application);
361 m_availableApplications.append(application);
364
362
@@ -367,7 +365,7 @@ void ApplicationManager::buildListOfAvailableApplications()
367 application->setName("Facebook");
365 application->setName("Facebook");
368 application->setIcon(QUrl("facebook"));
366 application->setIcon(QUrl("facebook"));
369 application->setStage(ApplicationInfo::SideStage);
367 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()));
371 generateQmlStrings(application);
369 generateQmlStrings(application);
372 m_availableApplications.append(application);
370 m_availableApplications.append(application);
373
371
@@ -375,7 +373,7 @@ void ApplicationManager::buildListOfAvailableApplications()
375 application->setAppId("webbrowser-app");
373 application->setAppId("webbrowser-app");
376 application->setName("Browser");
374 application->setName("Browser");
377 application->setIcon(QUrl("browser"));
375 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()));
379 generateQmlStrings(application);
377 generateQmlStrings(application);
380 m_availableApplications.append(application);
378 m_availableApplications.append(application);
381
379
@@ -384,7 +382,7 @@ void ApplicationManager::buildListOfAvailableApplications()
384 application->setName("Twitter");
382 application->setName("Twitter");
385 application->setIcon(QUrl("twitter"));
383 application->setIcon(QUrl("twitter"));
386 application->setStage(ApplicationInfo::SideStage);
384 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()));
388 generateQmlStrings(application);
386 generateQmlStrings(application);
389 m_availableApplications.append(application);
387 m_availableApplications.append(application);
390
388
@@ -392,7 +390,7 @@ void ApplicationManager::buildListOfAvailableApplications()
392 application->setAppId("gmail-webapp");
390 application->setAppId("gmail-webapp");
393 application->setName("GMail");
391 application->setName("GMail");
394 application->setIcon(QUrl("gmail"));
392 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()));
396 m_availableApplications.append(application);
394 m_availableApplications.append(application);
397
395
398 application = new ApplicationInfo(this);
396 application = new ApplicationInfo(this);
@@ -400,7 +398,7 @@ void ApplicationManager::buildListOfAvailableApplications()
400 application->setName("Weather");
398 application->setName("Weather");
401 application->setIcon(QUrl("weather"));
399 application->setIcon(QUrl("weather"));
402 application->setStage(ApplicationInfo::SideStage);
400 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()));
404 generateQmlStrings(application);
402 generateQmlStrings(application);
405 m_availableApplications.append(application);
403 m_availableApplications.append(application);
406
404
@@ -409,7 +407,7 @@ void ApplicationManager::buildListOfAvailableApplications()
409 application->setName("Notepad");
407 application->setName("Notepad");
410 application->setIcon(QUrl("notepad"));
408 application->setIcon(QUrl("notepad"));
411 application->setStage(ApplicationInfo::SideStage);
409 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()));
413 m_availableApplications.append(application);
411 m_availableApplications.append(application);
414
412
415 application = new ApplicationInfo(this);
413 application = new ApplicationInfo(this);
@@ -417,7 +415,7 @@ void ApplicationManager::buildListOfAvailableApplications()
417 application->setName("Calendar");
415 application->setName("Calendar");
418 application->setIcon(QUrl("calendar"));
416 application->setIcon(QUrl("calendar"));
419 application->setStage(ApplicationInfo::SideStage);
417 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()));
421 m_availableApplications.append(application);
419 m_availableApplications.append(application);
422
420
423 application = new ApplicationInfo(this);
421 application = new ApplicationInfo(this);
@@ -425,21 +423,21 @@ void ApplicationManager::buildListOfAvailableApplications()
425 application->setName("Media Player");
423 application->setName("Media Player");
426 application->setIcon(QUrl("mediaplayer-app"));
424 application->setIcon(QUrl("mediaplayer-app"));
427 application->setFullscreen(true);
425 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()));
429 m_availableApplications.append(application);
427 m_availableApplications.append(application);
430
428
431 application = new ApplicationInfo(this);
429 application = new ApplicationInfo(this);
432 application->setAppId("evernote");
430 application->setAppId("evernote");
433 application->setName("Evernote");
431 application->setName("Evernote");
434 application->setIcon(QUrl("evernote"));
432 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()));
436 m_availableApplications.append(application);
434 m_availableApplications.append(application);
437
435
438 application = new ApplicationInfo(this);
436 application = new ApplicationInfo(this);
439 application->setAppId("map");
437 application->setAppId("map");
440 application->setName("Map");
438 application->setName("Map");
441 application->setIcon(QUrl("map"));
439 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()));
443 generateQmlStrings(application);
441 generateQmlStrings(application);
444 m_availableApplications.append(application);
442 m_availableApplications.append(application);
445
443
@@ -447,24 +445,28 @@ void ApplicationManager::buildListOfAvailableApplications()
447 application->setAppId("pinterest");
445 application->setAppId("pinterest");
448 application->setName("Pinterest");
446 application->setName("Pinterest");
449 application->setIcon(QUrl("pinterest"));
447 application->setIcon(QUrl("pinterest"));
448+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
450 m_availableApplications.append(application);
449 m_availableApplications.append(application);
451
450
452 application = new ApplicationInfo(this);
451 application = new ApplicationInfo(this);
453 application->setAppId("soundcloud");
452 application->setAppId("soundcloud");
454 application->setName("SoundCloud");
453 application->setName("SoundCloud");
455 application->setIcon(QUrl("soundcloud"));
454 application->setIcon(QUrl("soundcloud"));
455+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
456 m_availableApplications.append(application);
456 m_availableApplications.append(application);
457
457
458 application = new ApplicationInfo(this);
458 application = new ApplicationInfo(this);
459 application->setAppId("wikipedia");
459 application->setAppId("wikipedia");
460 application->setName("Wikipedia");
460 application->setName("Wikipedia");
461 application->setIcon(QUrl("wikipedia"));
461 application->setIcon(QUrl("wikipedia"));
462+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
462 m_availableApplications.append(application);
463 m_availableApplications.append(application);
463
464
464 application = new ApplicationInfo(this);
465 application = new ApplicationInfo(this);
465 application->setAppId("youtube");
466 application->setAppId("youtube");
466 application->setName("YouTube");
467 application->setName("YouTube");
467 application->setIcon(QUrl("youtube"));
468 application->setIcon(QUrl("youtube"));
469+ application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
468 m_availableApplications.append(application);
470 m_availableApplications.append(application);
469}
471}
470
472