on xenial help sign in
Rework how we set the ranges so we get some more asynchronousity from item views Fixes: #1384374
05c7dcaAlbert Astals
plugins/Dash/abstractdashview.cpp+24−5
@@ -16,13 +16,12 @@
16
16
17#include "abstractdashview.h"
17#include "abstractdashview.h"
18
18
19−static const qreal bufferRatio = 0.5;
20−
21AbstractDashView::AbstractDashView()
19AbstractDashView::AbstractDashView()
22 : m_delegateModel(nullptr)
20 : m_delegateModel(nullptr)
23 , m_asyncRequestedIndex(-1)
21 , m_asyncRequestedIndex(-1)
24 , m_columnSpacing(0)
22 , m_columnSpacing(0)
25 , m_rowSpacing(0)
23 , m_rowSpacing(0)
24+ , m_buffer(320) // Same value used in qquickitemview.cpp in Qt 5.4
26 , m_displayMarginBeginning(0)
25 , m_displayMarginBeginning(0)
27 , m_displayMarginEnd(0)
26 , m_displayMarginEnd(0)
28 , m_needsRelayout(false)
27 , m_needsRelayout(false)
@@ -112,6 +111,27 @@ void AbstractDashView::setRowSpacing(qreal rowSpacing)
112 }
111 }
113}
112}
114
113
114+int AbstractDashView::cacheBuffer() const
115+{
116+ return m_buffer;
117+}
118+
119+void AbstractDashView::setCacheBuffer(int buffer)
120+{
121+ if (buffer < 0) {
122+ qmlInfo(this) << "Cannot set a negative cache buffer";
123+ return;
124+ }
125+
126+ if (m_buffer != buffer) {
127+ m_buffer = buffer;
128+ if (isComponentComplete()) {
129+ polish();
130+ }
131+ emit cacheBufferChanged();
132+ }
133+}
134+
115qreal AbstractDashView::displayMarginBeginning() const
135qreal AbstractDashView::displayMarginBeginning() const
116{
136{
117 return m_displayMarginBeginning;
137 return m_displayMarginBeginning;
@@ -160,9 +180,8 @@ void AbstractDashView::refill()
160
180
161 const qreal from = -m_displayMarginBeginning;
181 const qreal from = -m_displayMarginBeginning;
162 const qreal to = height() + m_displayMarginEnd;
182 const qreal to = height() + m_displayMarginEnd;
163− const qreal buffer = (to - from) * bufferRatio;
164− const qreal bufferFrom = from - buffer;
183+ const qreal bufferFrom = from - m_buffer;
165− const qreal bufferTo = to + buffer;
184+ const qreal bufferTo = to + m_buffer;
166
185
167 bool added = addVisibleItems(from, to, false);
186 bool added = addVisibleItems(from, to, false);
168 bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
187 bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
plugins/Dash/abstractdashview.h+6
@@ -35,6 +35,7 @@ class AbstractDashView : public QQuickItem
35 Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
35 Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
36 Q_PROPERTY(qreal columnSpacing READ columnSpacing WRITE setColumnSpacing NOTIFY columnSpacingChanged)
36 Q_PROPERTY(qreal columnSpacing READ columnSpacing WRITE setColumnSpacing NOTIFY columnSpacingChanged)
37 Q_PROPERTY(qreal rowSpacing READ rowSpacing WRITE setRowSpacing NOTIFY rowSpacingChanged)
37 Q_PROPERTY(qreal rowSpacing READ rowSpacing WRITE setRowSpacing NOTIFY rowSpacingChanged)
38+ Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged)
38 Q_PROPERTY(qreal displayMarginBeginning READ displayMarginBeginning
39 Q_PROPERTY(qreal displayMarginBeginning READ displayMarginBeginning
39 WRITE setDisplayMarginBeginning
40 WRITE setDisplayMarginBeginning
40 NOTIFY displayMarginBeginningChanged)
41 NOTIFY displayMarginBeginningChanged)
@@ -61,6 +62,9 @@ public:
61 qreal rowSpacing() const;
62 qreal rowSpacing() const;
62 void setRowSpacing(qreal rowSpacing);
63 void setRowSpacing(qreal rowSpacing);
63
64
65+ int cacheBuffer() const;
66+ void setCacheBuffer(int);
67+
64 qreal displayMarginBeginning() const;
68 qreal displayMarginBeginning() const;
65 void setDisplayMarginBeginning(qreal);
69 void setDisplayMarginBeginning(qreal);
66
70
@@ -72,6 +76,7 @@ Q_SIGNALS:
72 void delegateChanged();
76 void delegateChanged();
73 void columnSpacingChanged();
77 void columnSpacingChanged();
74 void rowSpacingChanged();
78 void rowSpacingChanged();
79+ void cacheBufferChanged();
75 void displayMarginBeginningChanged();
80 void displayMarginBeginningChanged();
76 void displayMarginEndChanged();
81 void displayMarginEndChanged();
77
82
@@ -117,6 +122,7 @@ private:
117
122
118 int m_columnSpacing;
123 int m_columnSpacing;
119 int m_rowSpacing;
124 int m_rowSpacing;
125+ int m_buffer;
120 qreal m_displayMarginBeginning;
126 qreal m_displayMarginBeginning;
121 qreal m_displayMarginEnd;
127 qreal m_displayMarginEnd;
122 bool m_needsRelayout;
128 bool m_needsRelayout;
plugins/Dash/listviewwithpageheader.cpp+7−2
@@ -334,13 +334,18 @@ qreal ListViewWithPageHeader::headerItemShownHeight() const
334 return m_headerItemShownHeight;
334 return m_headerItemShownHeight;
335}
335}
336
336
337−qreal ListViewWithPageHeader::cacheBuffer() const
337+int ListViewWithPageHeader::cacheBuffer() const
338{
338{
339 return m_cacheBuffer;
339 return m_cacheBuffer;
340}
340}
341
341
342−void ListViewWithPageHeader::setCacheBuffer(qreal cacheBuffer)
342+void ListViewWithPageHeader::setCacheBuffer(int cacheBuffer)
343{
343{
344+ if (cacheBuffer < 0) {
345+ qmlInfo(this) << "Cannot set a negative cache buffer";
346+ return;
347+ }
348+
344 if (cacheBuffer != m_cacheBuffer) {
349 if (cacheBuffer != m_cacheBuffer) {
345 m_cacheBuffer = cacheBuffer;
350 m_cacheBuffer = cacheBuffer;
346 Q_EMIT cacheBufferChanged();
351 Q_EMIT cacheBufferChanged();
plugins/Dash/listviewwithpageheader.h+4−4
@@ -54,7 +54,7 @@ class ListViewWithPageHeader : public QQuickFlickable, public QQuickItemChangeLi
54 Q_PROPERTY(bool forceNoClip READ forceNoClip WRITE setForceNoClip NOTIFY forceNoClipChanged)
54 Q_PROPERTY(bool forceNoClip READ forceNoClip WRITE setForceNoClip NOTIFY forceNoClipChanged)
55 Q_PROPERTY(int stickyHeaderHeight READ stickyHeaderHeight NOTIFY stickyHeaderHeightChanged)
55 Q_PROPERTY(int stickyHeaderHeight READ stickyHeaderHeight NOTIFY stickyHeaderHeightChanged)
56 Q_PROPERTY(qreal headerItemShownHeight READ headerItemShownHeight NOTIFY headerItemShownHeightChanged)
56 Q_PROPERTY(qreal headerItemShownHeight READ headerItemShownHeight NOTIFY headerItemShownHeightChanged)
57− Q_PROPERTY(qreal cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged)
57+ Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged)
58
58
59 friend class ListViewWithPageHeaderTest;
59 friend class ListViewWithPageHeaderTest;
60 friend class ListViewWithPageHeaderTestSection;
60 friend class ListViewWithPageHeaderTestSection;
@@ -85,8 +85,8 @@ public:
85 int stickyHeaderHeight() const;
85 int stickyHeaderHeight() const;
86 qreal headerItemShownHeight() const;
86 qreal headerItemShownHeight() const;
87
87
88− qreal cacheBuffer() const;
88+ int cacheBuffer() const;
89− void setCacheBuffer(qreal cacheBuffer);
89+ void setCacheBuffer(int cacheBuffer);
90
90
91 Q_INVOKABLE void positionAtBeginning();
91 Q_INVOKABLE void positionAtBeginning();
92 Q_INVOKABLE void showHeader();
92 Q_INVOKABLE void showHeader();
@@ -204,7 +204,7 @@ private:
204 bool m_forceNoClip;
204 bool m_forceNoClip;
205 bool m_inLayout;
205 bool m_inLayout;
206 bool m_inContentHeightKeepHeaderShown;
206 bool m_inContentHeightKeepHeaderShown;
207− qreal m_cacheBuffer;
207+ int m_cacheBuffer;
208
208
209 // Qt 5.0 doesn't like releasing the items just after itemCreated
209 // Qt 5.0 doesn't like releasing the items just after itemCreated
210 // so we delay the releasing until the next updatePolish
210 // so we delay the releasing until the next updatePolish
qml/Components/ResponsiveVerticalJournal.qml+1
@@ -45,6 +45,7 @@ Item {
45 property alias rowSpacing: verticalJournalView.rowSpacing
45 property alias rowSpacing: verticalJournalView.rowSpacing
46 property alias model: verticalJournalView.model
46 property alias model: verticalJournalView.model
47 property alias delegate: verticalJournalView.delegate
47 property alias delegate: verticalJournalView.delegate
48+ property alias cacheBuffer: verticalJournalView.cacheBuffer
48 property alias displayMarginBeginning: verticalJournalView.displayMarginBeginning
49 property alias displayMarginBeginning: verticalJournalView.displayMarginBeginning
49 property alias displayMarginEnd: verticalJournalView.displayMarginEnd
50 property alias displayMarginEnd: verticalJournalView.displayMarginEnd
50
51
qml/Dash/CardGrid.qml+1−2
@@ -50,7 +50,7 @@ DashRenderer {
50 model: root.model
50 model: root.model
51 displayMarginBeginning: root.displayMarginBeginning
51 displayMarginBeginning: root.displayMarginBeginning
52 displayMarginEnd: root.displayMarginEnd
52 displayMarginEnd: root.displayMarginEnd
53− cacheBuffer: 0
53+ cacheBuffer: root.cacheBuffer
54 interactive: false
54 interactive: false
55 delegate: Item {
55 delegate: Item {
56 width: grid.cellWidth
56 width: grid.cellWidth
@@ -59,7 +59,6 @@ DashRenderer {
59 id: loader
59 id: loader
60 sourceComponent: cardTool.cardComponent
60 sourceComponent: cardTool.cardComponent
61 anchors.horizontalCenter: parent.horizontalCenter
61 anchors.horizontalCenter: parent.horizontalCenter
62− asynchronous: true
63 onLoaded: {
62 onLoaded: {
64 item.objectName = "delegate" + index;
63 item.objectName = "delegate" + index;
65 item.width = Qt.binding(function() { return cardTool.cardWidth; });
64 item.width = Qt.binding(function() { return cardTool.cardWidth; });
qml/Dash/CardVerticalJournal.qml+1
@@ -59,6 +59,7 @@ DashRenderer {
59 rowSpacing: minimumColumnSpacing
59 rowSpacing: minimumColumnSpacing
60 columnWidth: cardTool.cardWidth
60 columnWidth: cardTool.cardWidth
61
61
62+ cacheBuffer: root.cacheBuffer
62 displayMarginBeginning: root.displayMarginBeginning
63 displayMarginBeginning: root.displayMarginBeginning
63 displayMarginEnd: root.displayMarginEnd
64 displayMarginEnd: root.displayMarginEnd
64
65
qml/Dash/DashContent.qml+2
@@ -163,6 +163,7 @@ Item {
163
163
164 delegate:
164 delegate:
165 Loader {
165 Loader {
166+ id: loader
166 width: ListView.view.width
167 width: ListView.view.width
167 height: ListView.view.height
168 height: ListView.view.height
168 opacity: { // hide delegate if offscreen
169 opacity: { // hide delegate if offscreen
@@ -191,6 +192,7 @@ Item {
191 dashContent.scopeLoaded(item.scope.id)
192 dashContent.scopeLoaded(item.scope.id)
192 item.paginationCount = Qt.binding(function() { return dashContentList.count } )
193 item.paginationCount = Qt.binding(function() { return dashContentList.count } )
193 item.paginationIndex = Qt.binding(function() { return dashContentList.currentIndex } )
194 item.paginationIndex = Qt.binding(function() { return dashContentList.currentIndex } )
195+ item.visibleToParent = Qt.binding(function() { return loader.opacity != 0 });
194 item.holdingList = dashContentList;
196 item.holdingList = dashContentList;
195 item.forceNonInteractive = Qt.binding(function() { return dashContent.forceNonInteractive } )
197 item.forceNonInteractive = Qt.binding(function() { return dashContent.forceNonInteractive } )
196 }
198 }
qml/Dash/DashRenderer.qml+2
@@ -23,6 +23,8 @@ Item {
23
23
24 property int collapsedItemCount: -1
24 property int collapsedItemCount: -1
25
25
26+ property int cacheBuffer: 0
27+
26 property int displayMarginBeginning: 0
28 property int displayMarginBeginning: 0
27
29
28 property int displayMarginEnd: 0
30 property int displayMarginEnd: 0
qml/Dash/GenericScopeView.qml+45−19
@@ -38,6 +38,7 @@ FocusScope {
38 readonly property alias subPageShown: subPageLoader.subPageShown
38 readonly property alias subPageShown: subPageLoader.subPageShown
39 property int paginationCount: 0
39 property int paginationCount: 0
40 property int paginationIndex: 0
40 property int paginationIndex: 0
41+ property bool visibleToParent: false
41 property alias pageHeaderTotallyVisible: categoryView.pageHeaderTotallyVisible
42 property alias pageHeaderTotallyVisible: categoryView.pageHeaderTotallyVisible
42 property var holdingList: null
43 property var holdingList: null
43
44
@@ -357,6 +358,7 @@ FocusScope {
357 Connections {
358 Connections {
358 target: scopeView
359 target: scopeView
359 onIsCurrentChanged: rendererLoader.updateRanges();
360 onIsCurrentChanged: rendererLoader.updateRanges();
361+ onVisibleToParentChanged: rendererLoader.updateRanges();
360 }
362 }
361 Connections {
363 Connections {
362 target: holdingList
364 target: holdingList
@@ -364,7 +366,9 @@ FocusScope {
364 }
366 }
365
367
366 function updateRanges() {
368 function updateRanges() {
369+ // Don't want to create stress by requesting more items during scope
370+ // changes so unless you're not part of the visible scopes just return
367− if (holdingList && holdingList.moving) {
371+ if (holdingList && holdingList.moving && !scopeView.visibleToParent) {
368 return;
372 return;
369 }
373 }
370
374
@@ -380,28 +384,50 @@ FocusScope {
380 }
384 }
381
385
382 if (item && item.hasOwnProperty("displayMarginBeginning")) {
386 if (item && item.hasOwnProperty("displayMarginBeginning")) {
383− // A item view is considered visible from
387+ // A item view creates its delegates synchronously from
384 // -displayMarginBeginning
388 // -displayMarginBeginning
385 // to
389 // to
386− // height + item.displayMarginEnd
390+ // height + displayMarginEnd
391+ // Around that area it adds the cacheBuffer area where delegates are created async
392+ //
387 // We adjust displayMarginBeginning and displayMarginEnd so
393 // We adjust displayMarginBeginning and displayMarginEnd so
388− // * In non visible scopes only the viewport is considered visible
394+ // * In non visible scopes nothing is considered visible and we set cacheBuffer
389− // that way when you switch to it the visible items are there
395+ // so that creates the items that would be in the viewport asynchronously
390− // * For visible scopes we increase the visible range by categoryView.height * 1.5
396+ // * For the current scope set the visible range to the viewport and then
397+ // use cacheBuffer to create extra items for categoryView.height * 1.5
391− // in both directions to make scrolling nicer by mantaining a higher number of
398+ // to make scrolling nicer by mantaining a higher number of
392 // cached items
399 // cached items
393− // TODO Improvements
400+ // * For non current but visible scopes (i.e. when the user changes from one scope
394− // - For non visible scopes we should always have a visible range of 0 and
401+ // to the next, we set the visible range to the viewport so
395− // make sure the items in the viewport are created with the cache buffer feature
402+ // items are not culled (invisible) but still use no cacheBuffer
396− // - For visible scopes we should always the have a visible range be exactly the
403+ // (it will be set once the scope is the current one)
397− // viewport and make sure the rest of items are created with the cache buffer feature
404+ var displayMarginBeginning = baseItem.y;
398− // To be able to implement that feature VerticalJournal/AbstractDashView needs to
405+ displayMarginBeginning = -Math.max(-displayMarginBeginning, 0);
399− // make the cache buffer value setable externally
406+ displayMarginBeginning = -Math.min(-displayMarginBeginning, baseItem.height);
407+ displayMarginBeginning = Math.round(displayMarginBeginning);
408+ var displayMarginEnd = -baseItem.height + seeAll.height + categoryView.height - baseItem.y;
409+ displayMarginEnd = -Math.max(-displayMarginEnd, 0);
410+ displayMarginEnd = -Math.min(-displayMarginEnd, baseItem.height);
411+ displayMarginEnd = Math.round(displayMarginEnd);
412+ if (scopeView.isCurrent || scopeView.visibleToParent) {
413+ item.displayMarginBeginning = displayMarginBeginning;
414+ item.displayMarginEnd = displayMarginEnd;
400− var extraMargins = scopeView.isCurrent ? categoryView.height * 1.5 : 0;
415+ item.cacheBuffer = scopeView.isCurrent ? categoryView.height * 1.5 : 0;
401−
416+ } else {
402− item.displayMarginBeginning = Math.round(-Math.max(-baseItem.y - extraMargins, 0));
417+ var visibleRange = baseItem.height + displayMarginEnd + displayMarginBeginning;
403− item.displayMarginEnd = -Math.round(Math.max(baseItem.height - extraMargins - seeAll.height -
418+ if (visibleRange < 0) {
404− categoryView.height + baseItem.y, 0));
419+ item.displayMarginBeginning = displayMarginBeginning;
420+ item.displayMarginEnd = displayMarginEnd;
421+ item.cacheBuffer = 0;
422+ } else {
423+ // This should be visibleRange/2 in each of the properties
424+ // but some item views still (like GridView) like creating sync delegates even if
425+ // the visible range is 0 so let's make sure the visible range is negative
426+ item.displayMarginBeginning = displayMarginBeginning - visibleRange;
427+ item.displayMarginEnd = displayMarginEnd - visibleRange;
428+ item.cacheBuffer = visibleRange;
429+ }
430+ }
405 }
431 }
406 }
432 }
407 }
433 }
qml/Dash/ScopeListView.qml+4−2
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2014 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -20,5 +20,7 @@ import Dash 0.1
20ListViewWithPageHeader {
20ListViewWithPageHeader {
21 maximumFlickVelocity: height * 10
21 maximumFlickVelocity: height * 10
22 flickDeceleration: height * 2
22 flickDeceleration: height * 2
23+ // 1073741823 is s^30 -1. A quite big number so that you have "infinite" cache, but not so
24+ // big so that if you add if with itself you're outside the 2^31 int range
23− cacheBuffer: Number.MAX_VALUE
25+ cacheBuffer: 1073741823
24}
26}
tests/plugins/Dash/horizontaljournaltest.qml+1
@@ -25,6 +25,7 @@ Item {
25 rowHeight: 150
25 rowHeight: 150
26 columnSpacing: 10
26 columnSpacing: 10
27 rowSpacing: 10
27 rowSpacing: 10
28+ cacheBuffer: Math.max(0, (height + displayMarginEnd + displayMarginBeginning) / 2)
28
29
29 delegate: Rectangle {
30 delegate: Rectangle {
30 property real randomValue: Math.random()
31 property real randomValue: Math.random()
tests/plugins/Dash/listviewwithpageheadertest.cpp+1−1
@@ -1920,7 +1920,7 @@ private Q_SLOTS:
1920
1920
1921 void testAllCacheBuffer()
1921 void testAllCacheBuffer()
1922 {
1922 {
1923− lvwph->setCacheBuffer(std::numeric_limits<qreal>::max());
1923+ lvwph->setCacheBuffer(std::numeric_limits<int>::max());
1924 QTRY_COMPARE(lvwph->m_visibleItems.count(), 6);
1924 QTRY_COMPARE(lvwph->m_visibleItems.count(), 6);
1925 QCOMPARE(lvwph->m_firstVisibleIndex, 0);
1925 QCOMPARE(lvwph->m_firstVisibleIndex, 0);
1926 verifyItem(0, 50., 150., false);
1926 verifyItem(0, 50., 150., false);
tests/plugins/Dash/organicgridtest.qml+1
@@ -27,6 +27,7 @@ Item {
27 rowSpacing: 10
27 rowSpacing: 10
28 smallDelegateSize: Qt.size(90, 90)
28 smallDelegateSize: Qt.size(90, 90)
29 bigDelegateSize: Qt.size(180, 180)
29 bigDelegateSize: Qt.size(180, 180)
30+ cacheBuffer: Math.max(0, (height + displayMarginEnd + displayMarginBeginning) / 2)
30
31
31 delegate: Rectangle {
32 delegate: Rectangle {
32 property real randomValue: Math.random()
33 property real randomValue: Math.random()
tests/plugins/Dash/verticaljournaltest.qml+1
@@ -26,6 +26,7 @@ Item {
26 columnWidth: 150
26 columnWidth: 150
27 columnSpacing: 10
27 columnSpacing: 10
28 rowSpacing: 10
28 rowSpacing: 10
29+ cacheBuffer: Math.max(0, (height + displayMarginEnd + displayMarginBeginning) / 2)
29
30
30 delegate: Rectangle {
31 delegate: Rectangle {
31 property real randomValue: Math.random()
32 property real randomValue: Math.random()
tests/qmltests/Dash/tst_GenericScopeView.qml+1
@@ -59,6 +59,7 @@ Item {
59 GenericScopeView {
59 GenericScopeView {
60 id: genericScopeView
60 id: genericScopeView
61 anchors.fill: parent
61 anchors.fill: parent
62+ visibleToParent: true
62
63
63 UT.UnityTestCase {
64 UT.UnityTestCase {
64 id: testCase
65 id: testCase