~mzanetti/unity8/fix-1648251

« back to all changes in this revision

Viewing changes to qml/Stages/TransformedTabletSpreadDelegate.qml

  • Committer: Michael Zanetti
  • Date: 2016-12-01 12:20:34 UTC
  • mfrom: (2525.1.179 unity8)
  • Revision ID: michael.zanetti@canonical.com-20161201122034-cpvbf7webbhg2wph
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright 2014 Canonical Ltd.
3
 
 *
4
 
 * This program is free software; you can redistribute it and/or modify
5
 
 * it under the terms of the GNU Lesser General Public License as published by
6
 
 * the Free Software Foundation; version 3.
7
 
 *
8
 
 * This program is distributed in the hope that it will be useful,
9
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
 
 * GNU Lesser General Public License for more details.
12
 
 *
13
 
 * You should have received a copy of the GNU Lesser General Public License
14
 
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15
 
 *
16
 
 * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17
 
*/
18
 
 
19
 
import QtQuick 2.4
20
 
import Utils 0.1
21
 
import Ubuntu.Components 1.3
22
 
import Unity.Application 0.1
23
 
 
24
 
SpreadDelegate {
25
 
    id: root
26
 
 
27
 
    // Set this to true when this tile is selected in the spread. The animation will change to bring the tile to front.
28
 
    property bool selected: false
29
 
    // Set this to true when another tile in the spread is selected. The animation will change to fade this tile out.
30
 
    property bool otherSelected: false
31
 
    // Set this to true when this tile a currently active on either the MS or the SS.
32
 
    property bool active: false
33
 
 
34
 
    property int zIndex
35
 
    property real progress: 0
36
 
    property real animatedProgress: 0
37
 
 
38
 
    property real startDistance: units.gu(5)
39
 
    property real endDistance: units.gu(.5)
40
 
 
41
 
    property real startScale: 1.1
42
 
    property real endScale: 0.7
43
 
    property real dragStartScale: startScale + .2
44
 
 
45
 
    property real startAngle: 15
46
 
    property real endAngle: 5
47
 
 
48
 
    property bool isInSideStage: false
49
 
 
50
 
    property int stage: ApplicationInfoInterface.MainStage
51
 
 
52
 
    property int dragOffset: 0
53
 
    readonly property alias xTranslateAnimating: xTranslateAnimation.running
54
 
    readonly property bool offScreen: priv.xTranslate >= 0
55
 
 
56
 
    dropShadow: spreadView.active ||
57
 
                (active
58
 
                 && (root.stage == ApplicationInfoInterface.MainStage || !priv.shellIsLandscape)
59
 
                 && priv.xTranslate != 0)
60
 
 
61
 
    onSelectedChanged: {
62
 
        if (selected) {
63
 
            priv.snapshot();
64
 
        }
65
 
        priv.isSelected = selected;
66
 
    }
67
 
 
68
 
    onOtherSelectedChanged: {
69
 
        if (otherSelected) {
70
 
            priv.snapshot();
71
 
        }
72
 
        priv.otherSelected = otherSelected;
73
 
    }
74
 
 
75
 
    Connections {
76
 
        target: spreadView
77
 
 
78
 
        onPhaseChanged: {
79
 
            if (spreadView.phase == 1) {
80
 
                var phase2Progress = spreadView.positionMarker4 - (root.zIndex * spreadView.tileDistance / spreadView.width);
81
 
                priv.phase2StartTranslate = priv.easingAnimation(0, 1, 0, -spreadView.width + (root.zIndex * root.endDistance), phase2Progress);
82
 
                priv.phase2StartScale = priv.easingAnimation(0, 1, root.startScale, root.endScale, phase2Progress);
83
 
                priv.phase2StartAngle = priv.easingAnimation(0, 1, root.startAngle, root.endAngle, phase2Progress);
84
 
                priv.phase2StartTopMarginProgress = priv.easingAnimation(0, 1, 0, 1, phase2Progress);
85
 
            }
86
 
        }
87
 
    }
88
 
 
89
 
    // Required to be an item because we're using states on it.
90
 
    Item {
91
 
        id: priv
92
 
 
93
 
        // true if this is the next tile on the stack that comes in when dragging from the right
94
 
        property bool nextInStack: spreadView.nextInStack == model.index
95
 
        // true if the next tile in the stack is the nextInStack one. This one will be moved a bit to the left
96
 
        property bool movedActive: spreadView.nextZInStack == zIndex + 1
97
 
        property real animatedEndDistance: linearAnimation(0, 2, root.endDistance, 0, root.progress)
98
 
 
99
 
        property real phase2StartTranslate
100
 
        property real phase2StartScale
101
 
        property real phase2StartAngle
102
 
        property real phase2StartTopMarginProgress
103
 
 
104
 
        property bool isSelected: false
105
 
        property bool otherSelected: false
106
 
        property real selectedProgress
107
 
        property real selectedXTranslate
108
 
        property real selectedAngle
109
 
        property real selectedScale
110
 
        property real selectedOpacity
111
 
        property real selectedTopMarginProgress
112
 
 
113
 
        function snapshot() {
114
 
            selectedProgress = root.progress;
115
 
            selectedXTranslate = xTranslate;
116
 
            selectedAngle = angle;
117
 
            selectedScale = priv.scale;
118
 
            selectedOpacity = priv.opacityTransform;
119
 
            selectedTopMarginProgress = topMarginProgress;
120
 
        }
121
 
 
122
 
        // This calculates how much negative progress there can be if unwinding the spread completely
123
 
        // the progress for each tile starts at 0 when it crosses the right edge, so the later a tile comes in,
124
 
        // the bigger its negativeProgress can be.
125
 
        property real negativeProgress: {
126
 
            if (nextInStack && spreadView.phase < 2) {
127
 
                return 0;
128
 
            }
129
 
            return -root.zIndex * spreadView.tileDistance / spreadView.width;
130
 
        }
131
 
 
132
 
        function linearAnimation(startProgress, endProgress, startValue, endValue, progress) {
133
 
            // progress : progressDiff = value : valueDiff => value = progress * valueDiff / progressDiff
134
 
            return (progress - startProgress) * (endValue - startValue) / (endProgress - startProgress) + startValue;
135
 
        }
136
 
 
137
 
        function easingAnimation(startProgress, endProgress, startValue, endValue, progress) {
138
 
            helperEasingCurve.progress = progress - startProgress;
139
 
            helperEasingCurve.period = endProgress - startProgress;
140
 
            return helperEasingCurve.value * (endValue - startValue) + startValue;
141
 
        }
142
 
 
143
 
        Behavior on xTranslate {
144
 
            enabled: !spreadView.active &&
145
 
                     !snapAnimation.running &&
146
 
                     root.application.appId !== "unity8-dash" &&
147
 
                     spreadView.animateX &&
148
 
                     !spreadView.beingResized &&
149
 
                     priv.state !== "sideStage"
150
 
            UbuntuNumberAnimation {
151
 
                id: xTranslateAnimation
152
 
                duration: UbuntuAnimation.FastDuration
153
 
            }
154
 
        }
155
 
 
156
 
        property real xTranslate: {
157
 
            var newTranslate = 0;
158
 
 
159
 
            // selected app or opposite stage active app.
160
 
            if (isSelected || (otherSelected && root.active && spreadView.selectedDelegate
161
 
                        && spreadView.selectedDelegate.stage !== root.stage)) {
162
 
                if (root.stage == ApplicationInfoInterface.MainStage) {
163
 
                    return linearAnimation(selectedProgress, negativeProgress, selectedXTranslate, -spreadView.width, root.progress);
164
 
                } else {
165
 
                    return linearAnimation(selectedProgress, negativeProgress, selectedXTranslate, -spreadView.sideStageWidth, root.progress);
166
 
                }
167
 
            } else if (otherSelected) {
168
 
                return selectedXTranslate;
169
 
            }
170
 
 
171
 
            // The tile should move a bit to the left if a new one comes on top of it, but not for the Side Stage and not
172
 
            // when we're only dragging the side stage in on top of a main stage app
173
 
            var shouldMoveAway = spreadView.nextInStack >= 0 && priv.movedActive && root.stage === ApplicationInfoInterface.MainStage &&
174
 
                    topLevelSurfaceList.applicationAt(spreadView.nextInStack).stage === ApplicationInfoInterface.MainStage;
175
 
 
176
 
            if (active) {
177
 
                newTranslate -= root.width
178
 
                // Only do the hide animation for active apps in the mainstage, and not if we only drag the ss in
179
 
                if (spreadView.phase == 0 && shouldMoveAway) {
180
 
                    newTranslate += linearAnimation(0, spreadView.positionMarker2, 0, -units.gu(4), root.animatedProgress);
181
 
                }
182
 
                newTranslate += root.dragOffset;
183
 
            } else if (!spreadView.active && root.application.appId == "unity8-dash") {
184
 
                newTranslate -= root.width;
185
 
            }
186
 
 
187
 
            if (!spreadView.active) {
188
 
                return newTranslate;
189
 
            }
190
 
 
191
 
            var shadowOffset = units.gu(2);
192
 
 
193
 
            if (spreadView.phase == 0) {
194
 
                if (nextInStack) {
195
 
                    if (root.stage == ApplicationInfoInterface.MainStage) {
196
 
                        if (spreadView.sideStageVisible && root.progress > 0) {
197
 
                            // Move it so it appears from behind the side stage immediately
198
 
                            newTranslate += -spreadView.sideStageWidth;
199
 
                        }
200
 
                    }
201
 
 
202
 
                    if (root.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) {
203
 
                        // This is when we only drag the side stage in, without rotation or snapping
204
 
                        newTranslate = linearAnimation(0, spreadView.positionMarker2, 0, -spreadView.sideStageWidth, root.animatedProgress);
205
 
                    } else {
206
 
                        newTranslate += linearAnimation(0, spreadView.positionMarker2, 0, -spreadView.sideStageWidth * spreadView.snapPosition, root.animatedProgress);
207
 
                    }
208
 
                } else if (!root.active) {
209
 
                    newTranslate += shadowOffset;
210
 
                }
211
 
            }
212
 
 
213
 
            if (spreadView.phase == 1) {
214
 
                if (nextInStack) {
215
 
                    if (root.stage == ApplicationInfoInterface.MainStage) {
216
 
                        var startValue = -spreadView.sideStageWidth * spreadView.snapPosition + (spreadView.sideStageVisible ? -spreadView.sideStageWidth : 0);
217
 
                        newTranslate += linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, startValue, priv.phase2StartTranslate, root.animatedProgress);
218
 
                    } else {
219
 
                        var endValue = -spreadView.width + spreadView.width * root.zIndex / 6;
220
 
                        if (!spreadView.sideStageVisible) {
221
 
                            newTranslate += linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, -spreadView.sideStageWidth, priv.phase2StartTranslate, root.animatedProgress);
222
 
                        } else {
223
 
                            newTranslate += linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, -spreadView.sideStageWidth * spreadView.snapPosition, priv.phase2StartTranslate, root.animatedProgress);
224
 
                        }
225
 
                    }
226
 
                } else if (root.active) {
227
 
                    var startProgress = spreadView.positionMarker2 - (zIndex * spreadView.positionMarker2 / 2);
228
 
                    var endProgress = spreadView.positionMarker4 - (zIndex * spreadView.tileDistance / spreadView.width);
229
 
                    var startTranslate = -root.width + (shouldMoveAway ? -units.gu(4) : 0);
230
 
                    newTranslate = linearAnimation(startProgress, endProgress, startTranslate, priv.phase2StartTranslate, root.animatedProgress);
231
 
                } else {
232
 
                    var startProgress = spreadView.positionMarker2 - (zIndex * spreadView.positionMarker2 / 2);
233
 
                    var endProgress = spreadView.positionMarker4 - (zIndex * spreadView.tileDistance / spreadView.width);
234
 
                    newTranslate = linearAnimation(startProgress, endProgress, shadowOffset * Math.max(0, zIndex-2), priv.phase2StartTranslate, root.animatedProgress);
235
 
                }
236
 
            }
237
 
 
238
 
            if (spreadView.phase == 2) {
239
 
                if (easingCurve.value == 0 && !nextInStack) {
240
 
                    newTranslate = shadowOffset;
241
 
                } else {
242
 
                    newTranslate = -easingCurve.value * (spreadView.width - root.zIndex * animatedEndDistance);
243
 
                }
244
 
            }
245
 
 
246
 
            return newTranslate;
247
 
        }
248
 
 
249
 
        property real scale: {
250
 
            if (!spreadView.active) {
251
 
                return 1;
252
 
            }
253
 
 
254
 
            // selected app or opposite stage active app.
255
 
            if (isSelected || (otherSelected && root.active && spreadView.selectedDelegate
256
 
                        && spreadView.selectedDelegate.stage !== root.stage)) {
257
 
                return linearAnimation(selectedProgress, negativeProgress, selectedScale, 1, root.progress);
258
 
            } else if (otherSelected) {
259
 
                return selectedScale;
260
 
            }
261
 
 
262
 
            if (spreadView.phase == 0) {
263
 
                if (nextInStack) {
264
 
                    if (root.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) {
265
 
                        return 1;
266
 
                    } else {
267
 
                        var targetScale = root.dragStartScale - ((root.dragStartScale - 1) * spreadView.snapPosition);
268
 
                        return linearAnimation(0, spreadView.positionMarker2, root.dragStartScale, targetScale, root.animatedProgress);
269
 
                    }
270
 
                } else {
271
 
                    return 1;
272
 
                }
273
 
            }
274
 
 
275
 
            if (spreadView.phase == 1) {
276
 
                if (nextInStack) {
277
 
                    var startScale = 1;
278
 
                    if (root.stage !== ApplicationInfoInterface.SideStage || spreadView.sideStageVisible) {
279
 
                        startScale = root.dragStartScale - ((root.dragStartScale - 1) * spreadView.snapPosition);
280
 
                    }
281
 
                    return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, startScale, priv.phase2StartScale, root.animatedProgress);
282
 
                }
283
 
                var startProgress = spreadView.positionMarker2 - (zIndex * spreadView.positionMarker2 / 2);
284
 
                var endProgress = spreadView.positionMarker4 - (zIndex * spreadView.tileDistance / spreadView.width);
285
 
                return linearAnimation(startProgress, endProgress, 1, priv.phase2StartScale, root.animatedProgress);
286
 
            }
287
 
 
288
 
            if (spreadView.phase == 2) {
289
 
                return root.startScale - easingCurve.value * (root.startScale - root.endScale);
290
 
            }
291
 
 
292
 
            return 1;
293
 
        }
294
 
 
295
 
        property real angle: {
296
 
            if (!spreadView.active) {
297
 
                return 0;
298
 
            }
299
 
 
300
 
            // selected app or opposite stage active app.
301
 
            if (isSelected || (otherSelected && root.active && spreadView.selectedDelegate
302
 
                        && spreadView.selectedDelegate.stage !== root.stage)) {
303
 
                return linearAnimation(selectedProgress, negativeProgress, selectedAngle, 0, root.progress);
304
 
            } else if (otherSelected) {
305
 
                return selectedAngle;
306
 
            }
307
 
 
308
 
            // The tile should rotate a bit when another one comes on top, but not when only dragging the side stage in
309
 
            var shouldMoveAway = spreadView.nextInStack == -1 ||
310
 
                    spreadView.nextInStack >= 0 && priv.movedActive &&
311
 
                    (topLevelSurfaceList.applicationAt(spreadView.nextInStack).stage === ApplicationInfoInterface.MainStage ||
312
 
                     root.stage == ApplicationInfoInterface.SideStage);
313
 
 
314
 
            if (spreadView.phase == 0) {
315
 
                if (nextInStack) {
316
 
                    if (root.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) {
317
 
                        return 0;
318
 
                    } else {
319
 
                        return linearAnimation(0, spreadView.positionMarker2, root.startAngle, root.startAngle * (1-spreadView.snapPosition), root.animatedProgress);
320
 
                    }
321
 
                }
322
 
                if (shouldMoveAway) {
323
 
                    return linearAnimation(0, spreadView.positionMarker2, 0, root.startAngle * (1-spreadView.snapPosition), root.animatedProgress);
324
 
                }
325
 
            }
326
 
            if (spreadView.phase == 1) {
327
 
                if (nextInStack) {
328
 
                    if (root.stage == ApplicationInfoInterface.SideStage && !spreadView.sideStageVisible) {
329
 
                        return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, 0, priv.phase2StartAngle, root.animatedProgress);
330
 
                    } else {
331
 
                        return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4, root.startAngle * (1-spreadView.snapPosition), priv.phase2StartAngle, root.animatedProgress);
332
 
                    }
333
 
                }
334
 
                var startProgress = spreadView.positionMarker2 - (zIndex * spreadView.positionMarker2 / 2);
335
 
                var endProgress = spreadView.positionMarker4 - (zIndex * spreadView.tileDistance / spreadView.width);
336
 
                var startAngle = shouldMoveAway ? root.startAngle * (1-spreadView.snapPosition) : 0;
337
 
                return linearAnimation(startProgress, endProgress, startAngle, priv.phase2StartAngle, root.progress);
338
 
            }
339
 
            if (spreadView.phase == 2) {
340
 
                return root.startAngle - easingCurve.value * (root.startAngle - root.endAngle);
341
 
            }
342
 
 
343
 
            return 0;
344
 
        }
345
 
 
346
 
        property real opacityTransform: {
347
 
            // animate opacity for items not snapping into view.
348
 
            if (spreadView.phase !== 2) return 1;
349
 
            if (root.isSelected) return 1;
350
 
 
351
 
            if (otherSelected) {
352
 
                if (root.active && spreadView.selectedDelegate
353
 
                        && spreadView.selectedDelegate.stage !== root.stage) {
354
 
                    return 1;
355
 
                }
356
 
                return linearAnimation(selectedProgress, negativeProgress, selectedOpacity, 0, root.progress);
357
 
            }
358
 
            return 1;
359
 
        }
360
 
 
361
 
        property real topMarginProgress: {
362
 
            // selected app or opposite stage active app.
363
 
            if (isSelected || (otherSelected && root.active && spreadView.selectedDelegate
364
 
                        && spreadView.selectedDelegate.stage !== root.stage)) {
365
 
                return linearAnimation(selectedProgress, negativeProgress, selectedTopMarginProgress, 0, root.progress);
366
 
            }
367
 
 
368
 
            if (spreadView.phase == 0) {
369
 
                return 0;
370
 
            } else if (spreadView.phase == 1) {
371
 
                if (nextInStack) {
372
 
                    return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
373
 
                                           0, 1, root.progress);
374
 
                }
375
 
                var startProgress = spreadView.positionMarker2 - (zIndex * spreadView.positionMarker2 / 2);
376
 
                var endProgress = spreadView.positionMarker4 - (zIndex * spreadView.tileDistance / spreadView.width);
377
 
                return linearAnimation(startProgress, endProgress, 0, 1, root.progress);
378
 
            }
379
 
            return 1;
380
 
        }
381
 
 
382
 
        states: [
383
 
            State {
384
 
                name: "sideStage";
385
 
                when: root.isInSideStage && spreadView.shiftedContentX == 0 && spreadView.phase == 0
386
 
                PropertyChanges {
387
 
                    target: priv;
388
 
                    xTranslate: -spreadView.sideStageWidth + spreadView.sideStageWidth * (1-sideStage.progress)
389
 
                }
390
 
            }
391
 
        ]
392
 
    }
393
 
 
394
 
    transform: [
395
 
        Rotation {
396
 
            origin { x: 0; y: spreadView.height / 2 }
397
 
            axis { x: 0; y: 1; z: 0 }
398
 
            angle: priv.angle
399
 
        },
400
 
        Scale {
401
 
            origin { x: 0; y: spreadView.height / 2 }
402
 
            xScale: priv.scale
403
 
            yScale: xScale
404
 
        },
405
 
        Scale {
406
 
            origin { x: 0; y: (spreadView.height * priv.scale) + maximizedAppTopMargin * 3 }
407
 
            xScale: 1
408
 
            yScale: fullscreen ? 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height : 1
409
 
        },
410
 
        Translate {
411
 
            x: priv.xTranslate
412
 
        }
413
 
    ]
414
 
    opacity: priv.opacityTransform
415
 
 
416
 
    UbuntuNumberAnimation {
417
 
        id: fadeBackInAnimation
418
 
        target: root
419
 
        property: "opacity"
420
 
        duration: UbuntuAnimation.SlowDuration
421
 
        from: 0
422
 
        to: 1
423
 
    }
424
 
 
425
 
    EasingCurve {
426
 
        id: easingCurve
427
 
        type: EasingCurve.OutSine
428
 
        period: 1
429
 
        progress: root.progress
430
 
    }
431
 
 
432
 
    EasingCurve {
433
 
        id: helperEasingCurve
434
 
        type: easingCurve.type
435
 
        period: easingCurve.period
436
 
    }
437
 
}