To put it differently (illustrating why it may be working "as designed"):
ListView has two modes. In the first mode we have a list, and there is a highlighted "current item" somewhere, perhaps scrolled out of sight, but we are not tracking it anymore. By scrolling, for instance, we immediately enter this mode. In this mode, inserting new elements will not try to keep the highlight in view, even if the highlight was in view before inserting. Fair enough, if you are scrolling you must perhaps give up on tracking the current item.
The other mode happens after a particular item has been selected by the application by setting currentIndex. Now modifications of the list will cause the ListView to scroll to keep the highlight in view.
My problem was that:
ListView is in the first, non-tracking mode by default
My operations on the ListView model always moved currentIndex implicitly, in exactly the same way as I was intending to move it explicitly
My explicit setting of currentIndex, always to the value that it already had never triggered the tracking mode. This does perhaps not trigger onCurrentIndexChanged so it may be impossible to detect for the underlying code
I can think of five separate ways that each would fix the issue:
Set the ListView to the "tracking" mode on startup. Or:
On scrolling (or in any state/operation), whenever the current item is left entirely contained within the visible range, automatically enter "tracking" mode (especially if "hichlightFollowsCurrentItem"). (Preferred.) Or:
On model changes, whenever the current item is already within the visible range, automatically enter "tracking" mode before starting to visualize the model change. Or:
Find out and fix why this code doesn't fix the problem: "onCurrentIndexChanged: positionViewAtIndex(currentIndex, ListView.Contain)". Or:
A "setCurrentItem" method that always triggers tracking mode, even if we are setting the index to what it already is