QML ListView's positionViewAtIndex incorrectly modifies currentIndex
-
Hello,
I am working on a ListView-based component, which provides infinite scroll functionality with a deque acting as a ring-buffer feeding data to the model. This is done using PySide2.
The component is almost working. However, I am facing an issue with positioning the view after one of buffer operations. When the user zooms in using the mouse wheel, elements' widths are increased (it's a horizontal view). That's fine. When a certain size criterion is met, the buffer may be shrunk, which removes a certain number of items from the model on both ends. All items removed are not-visible. Before buffer modification occurs, current item's index is stored in a variable, then adjusted to accommodate removal of items from the buffer. This part works correctly. Then the view is positioned to make the entire operation invisible to the user.
This is where issues begin. First, I am setting the currentIndex of the view to a new value, which correctly points to the item that was in the center of the view before the buffer operation. That positions the view correctly, however there is a visible "sliding" animation from the first element of the model to the desired index. This is not desired. To fix this, I am using positionViewAtIndex method, which positions the view immediately, removing the undesired animation. So far so good. Unfortunately, in certain conditions, which I cannot clearly identify, the view is positioned slightly off. After the view is positioned the currentIndex changes by 1. If the user triggers the buffer operation again, the positioning will be off by 1 again. This way, by simply zooming in, the view is also moving to the right, which is not intentional.
I was debugging it for quite some time and established that if the desired index change occurs, it is always a result of the positionViewAtIndex call. I considered setting the currentItem again after the positioning, but that triggers the undesireable animation again.
All buffer operations and subsequent updates to the view are implemented using a Declarative State Machine. The code for buffer operation in question and view updates is as follows:
DSM.State { id: shrinking onEntered: { console.log("pre-shrinking current item:", __view.currentIndex, __view.currentItem.val) let current_index = __view.currentIndex console.log("saved current item:", current_index, __view.itemAtIndex(current_index). // buffer modified here. a comfortable number of items is maintained outside the view custom_model.shrink_to(__view.first_visible, __view.last_visible) // correct new index __view.currentIndex = current_index - __view.first_visible + custom_model.shift_by + 2 console.log("current index set to", __view.currentIndex, __view.itemAtIndex(__view.currentIndex).val) // below *sometimes* positions the view off by 1 element __view.positionViewAtIndex(__view.currentIndex, ListView.Center) console.log("current index after positioning", __view.currentIndex, __view.itemAtIndex(__view.currentIndex).val) // optionally tried to set the current index again - will position correctly but with undesired animation // __view.currentIndex = current_index - __view.first_visible + custom_model.shift_by + 2 // this is needed for other functionality to work later __view.update_visible_items() console.log("post-shrinking current item:", __view.currentIndex, __view.currentItem.val) } }
all the console.log calls are for debugging. This works, i.e. the index changes with the buffer operation, but the actual item it points to is the same (116):
qml: pre-shrinking current item: 114 116 qml: saved current item: 114 116 qml: current index set to 35 116 qml: current index after positioning 35 116 qml: post-shrinking current item: 35 116
... but sometimes:
qml: pre-shrinking current item: 35 116 qml: saved current item: 35 116 qml: current index set to 14 116 qml: current index after positioning 15 117 qml: post-shrinking current item: 15 117
So it seems like I am either stuck with the animation or with unstable zoom in behavior. Is there a way to solve this? I am open to setting the animation to instant update, if that is possible, however it would need to apply to currentIndex change only. I am satisfied with how animations work for other operations. Preferably though, I would like to know if positionViewAtIndex's behavior can be fixed somehow.
Thanks!
-
Just an update, not sure if this is the reason for the behavior described above, but I had two ListView properties set to values, which could be contributing to this:
preferredHighlightBegin: Math.floor(width/2) preferredHighlightEnd: Math.floor(width/2 + 1)
I noticed that to trigger the issue I had to resize the window, so it makes sense to me that once I resized the window the preferredHighlight area would be not in the center, while positionViewAtIndex would position the item exactly at the center, which would then cause currentIndex to be updated to the next item.