@@ -40,28 +40,79 @@ PreviewWidget {
4040 id: root
4141 implicitHeight: childrenRect.height
4242
43− Column {
43+ onIsCurrentPreviewChanged: ratingsList.updateRanges();
44+ onParentFlickableChanged: ratingsList.updateRanges();
45+
46+ Connections {
47+ target: parentFlickable
48+ onOriginYChanged: ratingsList.updateRanges();
49+ onContentYChanged: ratingsList.updateRanges();
50+ onHeightChanged: ratingsList.updateRanges();
51+ onContentHeightChanged: ratingsList.updateRanges();
52+ }
53+
54+ ListView {
55+ id: ratingsList
4456 anchors { left: parent.left; right: parent.right; }
45− visible: reviewsRepeater.count > 0
57+ height: contentHeight
58+ interactive: false
59+
60+ model: root.widgetData["reviews"]
61+
62+ delegate: PreviewRatingSingleDisplay {
63+ id: prsd
64+ objectName: "reviewItem" + index
4665
47− Repeater {
48− id: reviewsRepeater
49− objectName: "reviewsRepeater"
50− model: root.widgetData["reviews"]
66+ anchors { left: parent.left; right: parent.right; }
5167
52− delegate: PreviewRatingSingleDisplay {
53− objectName: "reviewItem" + index
68+ rating: modelData["rating"] || -1
69+ author: modelData["author"] || ""
70+ review: modelData["review"] || ""
71+ urlIconEmpty: widgetData["rating-icon-empty"]
72+ urlIconFull: widgetData["rating-icon-full"]
73+ urlIconHalf: widgetData["rating-icon-half"]
74+ labelColor: scopeStyle ? scopeStyle.foreground : theme.palette.normal.baseText
75+ }
76+
77+ onContentHeightChanged: ratingsList.updateRanges();
5478
55− anchors { left: parent.left; right: parent.right; }
79+ function updateRanges() {
80+ var baseItem = root.parent;
81+ if (!parentFlickable || !baseItem) {
82+ ratingsList.displayMarginBeginning = 0;
83+ ratingsList.displayMarginEnd = 0;
84+ return;
85+ }
5686
57− rating: modelData["rating"] || -1
58− author: modelData["author"] || ""
59− review: modelData["review"] || ""
60− urlIconEmpty: widgetData["rating-icon-empty"]
61− urlIconFull: widgetData["rating-icon-full"]
62− urlIconHalf: widgetData["rating-icon-half"]
63− labelColor: scopeStyle ? scopeStyle.foreground : theme.palette.normal.baseText
87+ if (parentFlickable.moving) {
88+ // Do not update the range if we are overshooting up or down, since we'll come back
89+ // to the stable position and delete/create items without any reason
90+ if (parentFlickable.contentY < parentFlickable.originY) {
91+ return;
92+ } else if (parentFlickable.contentHeight - parentFlickable.originY > parentFlickable.height &&
93+ parentFlickable.contentY + parentFlickable.height > parentFlickable.contentHeight) {
94+ return;
95+ }
6496 }
97+
98+ // A item view creates its delegates synchronously from
99+ // -displayMarginBeginning
100+ // to
101+ // height + displayMarginEnd
102+ // Around that area it adds the cacheBuffer area where delegates are created async
103+ //
104+ // We adjust displayMarginEnd to be negative so that the range of created delegates matches
105+ // from the beginning of the list to the end of the viewport.
106+ // Ideally we would also use displayMarginBeginning
107+ // so that delegates at the beginning get destroyed but that causes issues with
108+ // listview and is not really necessary to provide the better experience we're after
109+ var itemYOnViewPort = baseItem.y - parentFlickable.contentY;
110+ var displayMarginEnd = -ratingsList.contentHeight + parentFlickable.height - itemYOnViewPort;
111+ displayMarginEnd = -Math.max(-displayMarginEnd, 0);
112+ displayMarginEnd = -Math.min(-displayMarginEnd, ratingsList.contentHeight);
113+ displayMarginEnd = Math.round(displayMarginEnd);
114+
115+ ratingsList.displayMarginEnd = displayMarginEnd;
65116 }
66117 }
67118}