~mzanetti/unity8/fix-left-edge-on-spread

« back to all changes in this revision

Viewing changes to qml/Dash/ScopesOverview.qml

  • Committer: Michael Zanetti
  • Date: 2015-01-12 11:21:17 UTC
  • mfrom: (1459.1.85 unity8)
  • Revision ID: michael.zanetti@canonical.com-20150112112117-0x9srs9dx0ndp60g
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (C) 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 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 General Public License for more details.
12
 
 *
13
 
 * You should have received a copy of the GNU General Public License
14
 
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
 
 */
16
 
 
17
 
import QtQuick 2.0
18
 
import Dash 0.1
19
 
import Ubuntu.Components 0.1
20
 
import "../Components"
21
 
 
22
 
Item {
23
 
    id: root
24
 
 
25
 
    // Properties set by parent
26
 
    property real progress: 0
27
 
    property var scope: null
28
 
    property int currentIndex: 0
29
 
    property real scopeScale: 1
30
 
 
31
 
    // Properties set and used by parent
32
 
    property alias currentTab: tabBar.currentTab
33
 
 
34
 
    // Properties used by parent
35
 
    readonly property bool processing: searchResultsViewer.processing || tempScopeItem.processing || previewListView.processing
36
 
    property bool growingDashFromPos: false
37
 
    readonly property bool searching: scope && scope.searchQuery == ""
38
 
    readonly property bool showingNonFavoriteScope: tempScopeItem.scope != null
39
 
    readonly property var dashItemEater: {
40
 
        if (!forceXYScalerEater && tabBar.currentTab == 0 && middleItems.count > 0) {
41
 
            var loaderItem = middleItems.itemAt(0).item;
42
 
            return loaderItem && loaderItem.currentItem ? loaderItem.currentItem : null;
43
 
        }
44
 
        return scopesOverviewXYScaler;
45
 
    }
46
 
    readonly property size allCardSize: {
47
 
        if (middleItems.count > 1) {
48
 
            var loaderItem = middleItems.itemAt(1).item;
49
 
            if (loaderItem) {
50
 
                var cardTool = loaderItem.cardTool;
51
 
                return Qt.size(cardTool.cardWidth, cardTool.cardHeight);
52
 
            }
53
 
        }
54
 
        return Qt.size(0, 0);
55
 
    }
56
 
 
57
 
    // Internal properties
58
 
    property bool forceXYScalerEater: false
59
 
 
60
 
    signal done()
61
 
    signal favoriteSelected(var scopeId)
62
 
    signal allFavoriteSelected(var scopeId)
63
 
    signal searchSelected(var scopeId, var result, var pos, var size)
64
 
 
65
 
    Connections {
66
 
        target: scope
67
 
        onOpenScope: {
68
 
            var itemPos = scopesOverviewXYScaler.restorePosition;
69
 
            var itemSize = scopesOverviewXYScaler.restoreSize;
70
 
            scopesOverviewXYScaler.scale = itemSize.width / scopesOverviewXYScaler.width;
71
 
            if (itemPos) {
72
 
                scopesOverviewXYScaler.x = itemPos.x -(scopesOverviewXYScaler.width - scopesOverviewXYScaler.width * scopesOverviewXYScaler.scale) / 2;
73
 
                scopesOverviewXYScaler.y = itemPos.y -(scopesOverviewXYScaler.height - scopesOverviewXYScaler.height * scopesOverviewXYScaler.scale) / 2;
74
 
            } else {
75
 
                scopesOverviewXYScaler.x = 0;
76
 
                scopesOverviewXYScaler.y = 0;
77
 
            }
78
 
            scopesOverviewXYScaler.opacity = 0;
79
 
            tempScopeItem.scope = scope;
80
 
            middleItems.overrideOpacity = 0;
81
 
            scopesOverviewXYScaler.scale = 1;
82
 
            scopesOverviewXYScaler.x = 0;
83
 
            scopesOverviewXYScaler.y = 0;
84
 
            scopesOverviewXYScaler.opacity = 1;
85
 
        }
86
 
        onGotoScope: {
87
 
            if (tabBar.currentTab == 0) {
88
 
                root.favoriteSelected(scopeId);
89
 
            } else {
90
 
                root.allFavoriteSelected(scopeId);
91
 
            }
92
 
            previewListView.open = false;
93
 
        }
94
 
    }
95
 
 
96
 
    Binding {
97
 
        target: scope
98
 
        property: "isActive"
99
 
        value: progress === 1
100
 
    }
101
 
 
102
 
    function closeTempScope() {
103
 
        if (tempScopeItem.scope) {
104
 
            root.scope.closeScope(tempScopeItem.scope);
105
 
            tempScopeItem.scope = null;
106
 
            tempScopeItem.backClicked()
107
 
        }
108
 
    }
109
 
 
110
 
    function animateDashFromAll(scopeId) {
111
 
        var currentScopePos = allScopeCardPosition(scopeId);
112
 
        if (currentScopePos) {
113
 
            showDashFromPos(currentScopePos, allCardSize);
114
 
        } else {
115
 
            console.log("Warning: Could not find Dash OverView All card position for scope", dashContent.currentScopeId);
116
 
        }
117
 
    }
118
 
 
119
 
    function showDashFromPos(itemPos, itemSize) {
120
 
        scopesOverviewXYScaler.scale = itemSize.width / scopesOverviewXYScaler.width;
121
 
        scopesOverviewXYScaler.x = itemPos.x -(scopesOverviewXYScaler.width - scopesOverviewXYScaler.width * scopesOverviewXYScaler.scale) / 2;
122
 
        scopesOverviewXYScaler.y = itemPos.y -(scopesOverviewXYScaler.height - scopesOverviewXYScaler.height * scopesOverviewXYScaler.scale) / 2;
123
 
        scopesOverviewXYScaler.opacity = 0;
124
 
        root.growingDashFromPos = true;
125
 
        scopesOverviewXYScaler.scale = 1;
126
 
        scopesOverviewXYScaler.x = 0;
127
 
        scopesOverviewXYScaler.y = 0;
128
 
        scopesOverviewXYScaler.opacity = 1;
129
 
    }
130
 
 
131
 
    function allScopeCardPosition(scopeId) {
132
 
        if (middleItems.count > 1) {
133
 
            var loaderItem = middleItems.itemAt(1).item;
134
 
            if (loaderItem) {
135
 
                var pos = loaderItem.scopeCardPosition(scopeId);
136
 
                return loaderItem.mapToItem(null, pos.x, pos.y);
137
 
            }
138
 
        }
139
 
    }
140
 
 
141
 
    function ensureAllScopeVisible(scopeId) {
142
 
        if (middleItems.count > 1) {
143
 
            var loaderItem = middleItems.itemAt(1).item;
144
 
            if (loaderItem) {
145
 
                var pos = loaderItem.scopeCardPosition(scopeId);
146
 
                loaderItem.contentY = Math.min(pos.y, loaderItem.contentHeight - loaderItem.height);
147
 
            }
148
 
        }
149
 
    }
150
 
 
151
 
    onProgressChanged: {
152
 
        if (progress == 0) {
153
 
            pageHeader.resetSearch();
154
 
            pageHeader.unfocus(); // Shouldn't the previous call do this too?
155
 
        }
156
 
    }
157
 
 
158
 
    ScopeStyle {
159
 
        id: overviewScopeStyle
160
 
        style: { "foreground-color" : "white",
161
 
                 "background-color" : "transparent",
162
 
                 "page-header": {
163
 
                    "background": "color:///transparent"
164
 
                 }
165
 
        }
166
 
    }
167
 
 
168
 
    DashBackground {
169
 
        anchors.fill: parent
170
 
        source: "graphics/dark_background.jpg"
171
 
    }
172
 
 
173
 
    Connections {
174
 
        target: pageHeader
175
 
        onSearchQueryChanged: {
176
 
            // Need this in order, otherwise something gets unhappy in rendering
177
 
            // of the overlay in carousels because the parent of the dash dies for
178
 
            // a moment, this way we make sure it's reparented first
179
 
            // by forceXYScalerEater making dashItemEater return scopesOverviewXYScaler
180
 
            // before we kill the previous parent by scope.searchQuery
181
 
            root.forceXYScalerEater = true;
182
 
            root.scope.searchQuery = pageHeader.searchQuery;
183
 
            root.forceXYScalerEater = false;
184
 
        }
185
 
    }
186
 
 
187
 
    Binding {
188
 
        target: pageHeader
189
 
        property: "searchQuery"
190
 
        value: scope ? scope.searchQuery : ""
191
 
    }
192
 
 
193
 
    Item {
194
 
        id: scopesOverviewContent
195
 
        x: previewListView.open ? -width : 0
196
 
        Behavior on x { UbuntuNumberAnimation { } }
197
 
        width: parent.width
198
 
        height: parent.height
199
 
 
200
 
        PageHeader {
201
 
            id: pageHeader
202
 
            objectName: "scopesOverviewPageHeader"
203
 
 
204
 
            readonly property real yDisplacement: pageHeader.height + tabBar.height + tabBar.anchors.margins
205
 
 
206
 
            y: {
207
 
                if (root.progress < 0.5) {
208
 
                    return -yDisplacement;
209
 
                } else {
210
 
                    return -yDisplacement + (root.progress - 0.5) * yDisplacement * 2;
211
 
                }
212
 
            }
213
 
            width: parent.width
214
 
            clip: true
215
 
            title: i18n.tr("Manage Scopes")
216
 
            scopeStyle: overviewScopeStyle
217
 
            showSignatureLine: false
218
 
            searchEntryEnabled: true
219
 
        }
220
 
 
221
 
        ScopesOverviewTab {
222
 
            id: tabBar
223
 
            anchors {
224
 
                left: parent.left
225
 
                right: parent.right
226
 
                top: pageHeader.bottom
227
 
                margins: units.gu(2)
228
 
            }
229
 
            height: units.gu(4)
230
 
 
231
 
            enabled: opacity == 1
232
 
            opacity: !scope || scope.searchQuery == "" ? 1 : 0
233
 
            Behavior on opacity { UbuntuNumberAnimation { } }
234
 
        }
235
 
 
236
 
        Repeater {
237
 
            id: middleItems
238
 
            objectName: "scopesOverviewRepeater"
239
 
            property real overrideOpacity: -1
240
 
            model: scope && scope.searchQuery == "" ? scope.categories : null
241
 
            delegate: Loader {
242
 
                id: loader
243
 
                objectName: "scopesOverviewRepeaterChild" + index
244
 
 
245
 
                height: {
246
 
                    if (index == 0) {
247
 
                        return root.height;
248
 
                    } else {
249
 
                        return root.height - pageHeader.height - tabBar.height - tabBar.anchors.margins - units.gu(2);
250
 
                    }
251
 
                }
252
 
                width: {
253
 
                    if (index == 0) {
254
 
                        return root.width / scopeScale;
255
 
                    } else {
256
 
                        return root.width;
257
 
                    }
258
 
                }
259
 
                x: {
260
 
                    if (index == 0) {
261
 
                        return (root.width - width) / 2;
262
 
                    } else {
263
 
                        return 0;
264
 
                    }
265
 
                }
266
 
                anchors {
267
 
                    bottom: scopesOverviewContent.bottom
268
 
                }
269
 
 
270
 
                scale: index == 0 ? scopeScale : 1
271
 
 
272
 
                opacity: {
273
 
                    if (middleItems.overrideOpacity >= 0)
274
 
                        return middleItems.overrideOpacity;
275
 
 
276
 
                    if (tabBar.currentTab != index)
277
 
                        return 0;
278
 
 
279
 
                    return index == 0 ? 1 : root.progress;
280
 
                }
281
 
                Behavior on opacity {
282
 
                    enabled: root.progress == 1
283
 
                    UbuntuNumberAnimation { }
284
 
                }
285
 
                enabled: opacity == 1
286
 
 
287
 
                clip: index == 1
288
 
 
289
 
                CardTool {
290
 
                    id: cardTool
291
 
                    objectName: "cardTool"
292
 
                    count: results.count
293
 
                    template: model.renderer
294
 
                    components: model.components
295
 
                    viewWidth: parent.width
296
 
                }
297
 
 
298
 
                source: {
299
 
                    if (index == 0 && categoryId == "favorites") return "ScopesOverviewFavorites.qml";
300
 
                    else if (index == 1 && categoryId == "all") return "ScopesOverviewAll.qml";
301
 
                    else return "";
302
 
                }
303
 
 
304
 
                onLoaded: {
305
 
                    item.model = Qt.binding(function() { return results; });
306
 
                    item.cardTool = cardTool;
307
 
                    if (index == 0) {
308
 
                        item.scopeWidth = Qt.binding(function() { return root.width; });
309
 
                        item.scopeHeight = Qt.binding(function() { return root.height; });
310
 
                        item.appliedScale = Qt.binding(function() { return loader.scale });
311
 
                        item.currentIndex = Qt.binding(function() { return root.currentIndex });
312
 
                    } else if (index == 1) {
313
 
                        item.extraHeight = bottomBar.height;
314
 
                    }
315
 
                }
316
 
 
317
 
                Connections {
318
 
                    target: loader.item
319
 
                    onClicked: {
320
 
                        pageHeader.unfocus();
321
 
                        if (tabBar.currentTab == 0) {
322
 
                            root.favoriteSelected(itemModel.scopeId);
323
 
                        } else {
324
 
                            var favoriteScopesItem = middleItems.itemAt(0).item;
325
 
                            var scopeIndex = favoriteScopesItem.model.scopeIndex(itemModel.scopeId);
326
 
                            if (scopeIndex >= 0) {
327
 
                                root.allFavoriteSelected(itemModel.scopeId);
328
 
                            } else {
329
 
                                // Will result in an openScope from root.scope
330
 
                                scopesOverviewXYScaler.restorePosition = item.mapToItem(null, 0, 0);
331
 
                                scopesOverviewXYScaler.restoreSize = allCardSize;
332
 
                                root.scope.activate(result);
333
 
                            }
334
 
                        }
335
 
                    }
336
 
                    onPressAndHold: {
337
 
                        // Preview can call openScope so make sure restorePosition and restoreSize are set
338
 
                        scopesOverviewXYScaler.restorePosition = undefined;
339
 
                        scopesOverviewXYScaler.restoreSize = allCardSize;
340
 
 
341
 
                        previewListView.model = target.model;
342
 
                        previewListView.currentIndex = -1;
343
 
                        previewListView.currentIndex = index;
344
 
                        previewListView.open = true;
345
 
                    }
346
 
                }
347
 
            }
348
 
        }
349
 
 
350
 
        GenericScopeView {
351
 
            id: searchResultsViewer
352
 
            objectName: "searchResultsViewer"
353
 
            anchors {
354
 
                top: pageHeader.bottom
355
 
                right: parent.right
356
 
                left: parent.left
357
 
                bottom: parent.bottom
358
 
            }
359
 
            scope: root.scope && root.scope.searchQuery != "" ? root.scope : null
360
 
            scopeStyle: overviewScopeStyle
361
 
            enabled: opacity == 1
362
 
            showPageHeader: false
363
 
            clip: true
364
 
            opacity: searchResultsViewer.scope ? 1 : 0
365
 
            isCurrent: true
366
 
            Behavior on opacity { UbuntuNumberAnimation { } }
367
 
 
368
 
            function itemClicked(index, result, item, itemModel, resultsModel, limitedCategoryItemCount) {
369
 
                pageHeader.unfocus();
370
 
                pageHeader.closePopup();
371
 
                if (itemModel.scopeId) {
372
 
                    // This can end up in openScope so save restorePosition and restoreSize
373
 
                    scopesOverviewXYScaler.restorePosition = item.mapToItem(null, 0, 0);
374
 
                    scopesOverviewXYScaler.restoreSize = Qt.size(item.width, item.height);
375
 
                    root.searchSelected(itemModel.scopeId, result, item.mapToItem(null, 0, 0), Qt.size(item.width, item.height));
376
 
                } else {
377
 
                    // Not a scope, just activate it
378
 
                    searchResultsViewer.scope.activate(result);
379
 
                }
380
 
            }
381
 
 
382
 
            function itemPressedAndHeld(index, itemModel, resultsModel, limitedCategoryItemCount) {
383
 
                if (itemModel.uri.indexOf("scope://") === 0) {
384
 
                    // Preview can call openScope so make sure restorePosition and restoreSize are set
385
 
                    scopesOverviewXYScaler.restorePosition = undefined;
386
 
                    scopesOverviewXYScaler.restoreSize = allCardSize;
387
 
 
388
 
                    previewListView.model = resultsModel;
389
 
                    previewListView.currentIndex = -1;
390
 
                    previewListView.currentIndex = index;
391
 
                    previewListView.open = true;
392
 
                }
393
 
            }
394
 
        }
395
 
 
396
 
        Rectangle {
397
 
            id: bottomBar
398
 
            objectName: "bottomBar"
399
 
            color: "black"
400
 
            height: units.gu(8)
401
 
            width: parent.width
402
 
            enabled: opacity == 1
403
 
            opacity: scope && scope.searchQuery == "" ? 1 : 0
404
 
            Behavior on opacity { UbuntuNumberAnimation { } }
405
 
            y: {
406
 
                if (root.progress < 0.5) {
407
 
                    return parent.height;
408
 
                } else {
409
 
                    return parent.height - (root.progress - 0.5) * height * 2;
410
 
                }
411
 
            }
412
 
 
413
 
            MouseArea {
414
 
                // Just eat any other press since this parent is black opaque
415
 
                anchors.fill: parent
416
 
            }
417
 
 
418
 
            AbstractButton {
419
 
                objectName: "scopesOverviewDoneButton"
420
 
                width: Math.max(label.width + units.gu(2), units.gu(10))
421
 
                height: units.gu(4)
422
 
                anchors {
423
 
                    left: parent.left
424
 
                    leftMargin: units.gu(2)
425
 
                    verticalCenter: parent.verticalCenter
426
 
                }
427
 
                Rectangle {
428
 
                    anchors.fill: parent
429
 
                    border.color: "white"
430
 
                    border.width: units.dp(1)
431
 
                    radius: units.dp(10)
432
 
                    color: parent.pressed ? Theme.palette.normal.baseText : "transparent"
433
 
                }
434
 
                Label {
435
 
                    id: label
436
 
                    anchors.centerIn: parent
437
 
                    text: i18n.tr("Done")
438
 
                    color: parent.pressed ? "black" : "white"
439
 
                }
440
 
                onClicked: root.done();
441
 
            }
442
 
 
443
 
            AbstractButton {
444
 
                objectName: "scopesOverviewStoreButton"
445
 
                width: Math.max(storeLabel.width, units.gu(10))
446
 
                height: units.gu(4)
447
 
                anchors {
448
 
                    right: parent.right
449
 
                    verticalCenter: parent.verticalCenter
450
 
                }
451
 
                Icon {
452
 
                    id: storeImage
453
 
                    name: "ubuntu-store-symbolic"
454
 
                    color: "white"
455
 
                    anchors.horizontalCenter: parent.horizontalCenter
456
 
                    width: units.gu(2)
457
 
                    height: units.gu(2)
458
 
                }
459
 
                Label {
460
 
                    id: storeLabel
461
 
                    anchors.horizontalCenter: parent.horizontalCenter
462
 
                    anchors.top: storeImage.bottom
463
 
                    text: i18n.tr("Store")
464
 
                    color: "white"
465
 
                }
466
 
                onClicked: {
467
 
                    // Just zoom from the middle
468
 
                    scopesOverviewXYScaler.restorePosition = undefined;
469
 
                    scopesOverviewXYScaler.restoreSize = allCardSize;
470
 
                    scope.performQuery("scope://com.canonical.scopes.clickstore");
471
 
                }
472
 
            }
473
 
        }
474
 
    }
475
 
 
476
 
    PreviewListView {
477
 
        id: previewListView
478
 
        objectName: "scopesOverviewPreviewListView"
479
 
        scope: root.scope
480
 
        scopeStyle: overviewScopeStyle
481
 
        showSignatureLine: false
482
 
        visible: x != width
483
 
        width: parent.width
484
 
        height: parent.height
485
 
        anchors.left: scopesOverviewContent.right
486
 
 
487
 
        onBackClicked: open = false
488
 
    }
489
 
 
490
 
    Item {
491
 
        id: scopesOverviewXYScaler
492
 
        width: parent.width
493
 
        height: parent.height
494
 
 
495
 
        clip: scale != 1.0
496
 
        enabled: scale == 1
497
 
 
498
 
        property bool animationsEnabled: root.showingNonFavoriteScope || root.growingDashFromPos
499
 
 
500
 
        property var restorePosition
501
 
        property var restoreSize
502
 
 
503
 
        Behavior on x {
504
 
            enabled: scopesOverviewXYScaler.animationsEnabled
505
 
            UbuntuNumberAnimation { }
506
 
        }
507
 
        Behavior on y {
508
 
            enabled: scopesOverviewXYScaler.animationsEnabled
509
 
            UbuntuNumberAnimation { }
510
 
        }
511
 
        Behavior on opacity {
512
 
            enabled: scopesOverviewXYScaler.animationsEnabled
513
 
            UbuntuNumberAnimation { }
514
 
        }
515
 
        Behavior on scale {
516
 
            enabled: scopesOverviewXYScaler.animationsEnabled
517
 
            UbuntuNumberAnimation {
518
 
                onRunningChanged: {
519
 
                    if (!running) {
520
 
                        if (root.showingNonFavoriteScope && scopesOverviewXYScaler.scale != 1) {
521
 
                            root.scope.closeScope(tempScopeItem.scope);
522
 
                            tempScopeItem.scope = null;
523
 
                        } else if (root.growingDashFromPos) {
524
 
                            root.growingDashFromPos = false;
525
 
                        }
526
 
                    }
527
 
                }
528
 
            }
529
 
        }
530
 
 
531
 
        DashBackground {
532
 
            anchors.fill: tempScopeItem
533
 
            visible: tempScopeItem.visible
534
 
            parent: tempScopeItem.parent
535
 
        }
536
 
 
537
 
        GenericScopeView {
538
 
            id: tempScopeItem
539
 
            objectName: "scopesOverviewTempScopeItem"
540
 
 
541
 
            width: parent.width
542
 
            height: parent.height
543
 
            clip: scale != 1.0
544
 
            visible: scope != null
545
 
            hasBackAction: true
546
 
            isCurrent: visible
547
 
            onBackClicked: {
548
 
                var v = scopesOverviewXYScaler.restoreSize.width / tempScopeItem.width;
549
 
                scopesOverviewXYScaler.scale = v;
550
 
                if (scopesOverviewXYScaler.restorePosition) {
551
 
                    scopesOverviewXYScaler.x = scopesOverviewXYScaler.restorePosition.x -(tempScopeItem.width - tempScopeItem.width * v) / 2;
552
 
                    scopesOverviewXYScaler.y = scopesOverviewXYScaler.restorePosition.y -(tempScopeItem.height - tempScopeItem.height * v) / 2;
553
 
                } else {
554
 
                    scopesOverviewXYScaler.x = 0;
555
 
                    scopesOverviewXYScaler.y = 0;
556
 
                }
557
 
                scopesOverviewXYScaler.opacity = 0;
558
 
                middleItems.overrideOpacity = -1;
559
 
            }
560
 
            // TODO Add tests for these connections
561
 
            Connections {
562
 
                target: tempScopeItem.scope
563
 
                onOpenScope: {
564
 
                    // TODO Animate the newly opened scope into the foreground (stacked on top of the current scope)
565
 
                    tempScopeItem.scope = scope;
566
 
                }
567
 
                onGotoScope: {
568
 
                    tempScopeItem.backClicked();
569
 
                    root.currentTab = 0;
570
 
                    root.scope.gotoScope(scopeId);
571
 
                }
572
 
            }
573
 
        }
574
 
    }
575
 
}