<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[ListView for chat message history: positionViewAtIndex doesn&#x27;t work reliably (+repro)]]></title><description><![CDATA[<p dir="auto">Hey folks. Building a messaging app in QML and want to use ListView for the message history.</p>
<p dir="auto">What I need from the widget:</p>
<ol>
<li>Ability to preserve scroll position for given data, so that when user changes channels I can stash away the scroll position of current channel, change the underlying data to the messages of new channel and restore the previously stashed scroll of the new channel (basically preserve scroll as data without the need to keep a bunch of widgets in memory, like 5 instances of ListView with their models)</li>
<li>Ability to jump to a particular item in the list</li>
<li>Support for items of variable height</li>
</ol>
<p dir="auto">I have been wrestling with ListView for god knows how long, I fix something up, all seems to work and then I come across a particular message contents and position combination where scroll jumping/restoration breaks again. Basically <code>positionViewAtIndex</code> doesn't work reliably.</p>
<p dir="auto">My question to you:</p>
<ol>
<li>Is ListView just not suitable for this use? Is there a better alternative or will I have to make my custom C++ impl?</li>
<li>Is this a genuine bug in LIstView or is <code>positionViewAtIndex</code> inherently not guaranteed to work reliably?</li>
</ol>
<p dir="auto">Run the reproducer with <code>qml Repro.qml</code>. Scroll down/up so that the jump target index is out of view and then click jump. Any index and position combination works correctly, except for index 1. If you scroll far away enough from index 1 the jump always misses.</p>
<p dir="auto">repro:</p>
<pre><code class="language-qml">// Run: qml Repro.qml
// Reproduced with Qt 6.8.2.
//
// 1. Start the application and leave the index at 1.
// 2. Click JUMP once: the requested rectangle is not aligned correctly.
// 3. Click JUMP again: it aligns correctly, without changing any heights.
// Restart the application to repeat from the initial state.
//
// With BottomToTop and ListView.End, the requested rectangle should align
// with the TOP of the viewport (unless constrained by a real list boundary).
// Index 1 has enough content on either side to permit that alignment.

import QtQuick
import QtQuick.Window
import QtQuick.Controls

Window {
    id: root
    visible: true
    width: 800
    height: 640
    title: "repro"
    color: "#202020"

    property int jumpCount: 0
    property string result: "Click JUMP once, then again. Expected target offset: 0 px."

    ListView {
        id: list
        anchors {
            top: parent.top
            left: parent.left
            right: parent.right
            bottom: controls.top
        }
        clip: true
        spacing: 0
        verticalLayoutDirection: ListView.BottomToTop

        model: [
            { height: 3425, color: "#8e44ad" },
            { height: 82, color: "#2980b9" },
            { height: 454, color: "#16806a" },
            { height: 176, color: "#b45f06" },
            { height: 64, color: "#a93226" },
            { height: 22, color: "#566573" },
            { height: 218, color: "#784212" },
            { height: 2000, color: "#333344" }
        ]

        delegate: Rectangle {
            id: rectangle
            required property int index
            required property var modelData

            width: list.width
            height: modelData.height
            color: modelData.color
            border.color: "white"
            border.width: 1

            Text {
                color: "white"
                font.pixelSize: 14
                text: "Index " + rectangle.index + " | height " + rectangle.height + " px"
            }
        }
    }

    Rectangle {
        id: controls
        anchors {
            left: parent.left
            right: parent.right
            bottom: parent.bottom
        }
        height: 81
        color: "#252525"

        Row {
            x: 8
            y: 6
            spacing: 8

            SpinBox {
                id: indexInput
                from: 0
                to: 7
                value: 1
                editable: true
            }

            Button {
                id: jumpButton
                text: "JUMP"
                onClicked: {
                    console.log("Geometry at JUMP:",
                                "window", root.width + "x" + root.height,
                                "viewport", list.width + "x" + list.height,
                                "(logical px), DPR", root.Screen.devicePixelRatio);
                    const index = indexInput.value;
                    list.positionViewAtIndex(index, ListView.End);

                    const item = list.itemAtIndex(index);
                    ++root.jumpCount;
                    if (!item) {
                        root.result = "Target delegate was not realized.";
                    } else {
                        const offset = item.y - list.contentY;
                        root.result = "Call " + root.jumpCount
                            + " | index " + index
                            + " | target.y " + item.y
                            + " | contentY " + list.contentY
                            + " | offset " + offset + " px";
                    }
                    console.log(root.result);
                }
            }
        }

        Text {
            x: 8
            y: 55
            width: parent.width - 16
            color: "white"
            font.pixelSize: 13
            elide: Text.ElideRight
            text: root.result
        }
    }
}
</code></pre>
<p dir="auto">My logs (first is jump from below, second is jump from above, the offset should be <code>0</code>, but it's not):</p>
<pre><code>qml: Geometry at JUMP: window 1276x1390 viewport 1276x1309 (logical px), DPR 1.5
qml: Call 1 | index 1 | target.y -3507 | contentY -4303 | offset 796 px
qml: Geometry at JUMP: window 1276x1390 viewport 1276x1309 (logical px), DPR 1.5
qml: Call 2 | index 1 | target.y 7477 | contentY 6681 | offset 796 px
</code></pre>
<p dir="auto">Would really appreciate any help.</p>
]]></description><link>https://forum.qt.io/topic/165191/listview-for-chat-message-history-positionviewatindex-doesn-t-work-reliably-repro</link><generator>RSS for Node</generator><lastBuildDate>Fri, 06 Nov 2026 10:37:07 GMT</lastBuildDate><atom:link href="https://forum.qt.io/topic/165191.rss" rel="self" type="application/rss+xml"/><pubDate>Tue, 20 Oct 2026 11:54:04 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to ListView for chat message history: positionViewAtIndex doesn&#x27;t work reliably (+repro) on Tue, 03 Nov 2026 10:25:29 GMT]]></title><description><![CDATA[<p dir="auto">This is an interesting issue with Qt Quick ListView, especially when working with variable-height delegates. The estimated delegate sizes can sometimes cause unexpected scroll positioning when jumping to items that are outside the visible area.</p>
<p dir="auto">Calling <code>positionViewAtIndex()</code> twice may help as a workaround, as the first call allows ListView to create nearby delegates and update its size estimates.</p>
]]></description><link>https://forum.qt.io/post/840743</link><guid isPermaLink="true">https://forum.qt.io/post/840743</guid><dc:creator><![CDATA[Jacob]]></dc:creator><pubDate>Tue, 03 Nov 2026 10:25:29 GMT</pubDate></item><item><title><![CDATA[Reply to ListView for chat message history: positionViewAtIndex doesn&#x27;t work reliably (+repro) on Mon, 02 Nov 2026 21:22:59 GMT]]></title><description><![CDATA[<p dir="auto">Hey! I retested your repro on Qt 6.7.3 and 6.11.2 on Windows and got exactly your numbers on both.</p>
<p dir="auto">Using ListView like this is fine, I use it in a similar way myself. My delegates all have the same height though, so I never hit this. With equal heights, every jump lands correctly.</p>
<p dir="auto">ListView doesn't create delegates that are far off screen, so it estimates their height from the ones it has. When delegate heights vary, that estimate can be way off (your index 0 is 3425 px, but it gets guessed at a few hundred), and the position calculation for the jump ends up wrong.</p>
<p dir="auto">Try calling positionViewAtIndex twice as a workaround:</p>
<pre><code class="language-qml">list.positionViewAtIndex(index, ListView.End);
list.positionViewAtIndex(index, ListView.End);
</code></pre>
<p dir="auto">The first call creates the delegates around the target, so on the second call ListView knows their real heights and positions correctly.</p>
]]></description><link>https://forum.qt.io/post/840740</link><guid isPermaLink="true">https://forum.qt.io/post/840740</guid><dc:creator><![CDATA[michalgrabarczyk]]></dc:creator><pubDate>Mon, 02 Nov 2026 21:22:59 GMT</pubDate></item></channel></rss>