ListView - currentIndex cannot be set in delegate with Component.onCompleted
-
Hello,
I would like to understand the underlying reason for the following behavior. There is such an exemplary piece of code:
import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15 Window { visible: true width: 500 height: 600 ListModel { id: myModel ListElement {name: "Item";} ListElement {name: "Item";} ListElement {name: "Item";} ListElement {name: "Item";} } ListView { id: listView width: parent.width height: 0.7 * parent.height signal test() model: myModel delegate: Rectangle { id: listDelegate color: listView.currentIndex == index ? "green" : "red" height: 50 width: listView.width Text { id: listDelegateText text: name anchors.centerIn: parent font.pointSize: 20 } Component.onCompleted: { # the part that is not working: listView.currentIndex = 2 } } } }
My expectation is that
currentIndex
should be set to2
. However, there is even no attempt to change the index. The problem is that:- other
ListView
properties can be changed form that part that is not working, so we clearly have access to the right reference ListView
currentIndex
can be changed in any other place of the code- starting an outside
Timer
fromonCompleted
part, even with the interval set to0
, also allows for thecurrentIndex
change
Given that all, the natural expectation is that the code above should also work. But it is not a case. I have started digging into the source code of QML and the only thing I have found is the following code:
qtdeclarative-dev/src/quick/items/qquickitemview.cppFxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, QQmlIncubator::IncubationMode incubationMode) { Q_Q(QQuickItemView); if (requestedIndex == modelIndex && incubationMode == QQmlIncubator::Asynchronous) return nullptr; for (int i=0; i<releasePendingTransition.count(); i++) { if (releasePendingTransition.at(i)->index == modelIndex && !releasePendingTransition.at(i)->isPendingRemoval()) { releasePendingTransition[i]->releaseAfterTransition = false; return releasePendingTransition.takeAt(i); } } inRequest = true; QObject* object = model->object(modelIndex, incubationMode); QQuickItem *item = qmlobject_cast<QQuickItem*>(object); if (!item) { if (!object) { if (requestedIndex == -1 && model->incubationStatus(modelIndex) == QQmlIncubator::Loading) { // The reason we didn't receive an item is because it's incubating async. We keep track // of this by assigning the index we're waiting for to 'requestedIndex'. This will e.g. let // the view avoid unnecessary layout calls until the item has been loaded. requestedIndex = modelIndex; } } else { model->release(object); if (!delegateValidated) { delegateValidated = true; QObject* delegate = q->delegate(); qmlWarning(delegate ? delegate : q) << QQuickItemView::tr("Delegate must be of Item type"); } } inRequest = false; return nullptr; } else { item->setParentItem(q->contentItem()); if (requestedIndex == modelIndex) requestedIndex = -1; FxViewItem *viewItem = newViewItem(modelIndex, item); if (viewItem) { viewItem->index = modelIndex; // do other set up for the new item that should not happen // until after bindings are evaluated initializeViewItem(viewItem); unrequestedItems.remove(item); } inRequest = false; return viewItem; } }
and
void QQuickItemView::setCurrentIndex(int index) { Q_D(QQuickItemView); if (d->inRequest) // currently creating item return; d->currentIndexCleared = (index == -1); d->applyPendingChanges(); if (index == d->currentIndex) return; if (isComponentComplete() && d->isValid()) { d->moveReason = QQuickItemViewPrivate::SetIndex; d->updateCurrent(index); } else if (d->currentIndex != index) { d->currentIndex = index; emit currentIndexChanged(); } }
There is that
inRequest
boolean variable that smells suspicously. It would mean that callingonCompleted
inside the delegate does not actually mean that the creation is fully completed.Generally, the questions:
- Am I correct that
inRequest
variable prevents from changingcurrentIndex
in the way shown on the first code snippet in this post? - Isn't that a bug?
- other
-
@KroMignon
So, the origin of that question is the following issue raised on SO:
https://stackoverflow.com/questions/63252698/in-listview-how-can-you-set-the-currentindex-or-currentitem-to-a-dynamically-cr#comment111851987_63252698I just started to wonder why it is not possible to change
currentIndex
inside delegateComponent.onCompleted
. What is the reason? The code I provided is as simple as possible just to expose the issue. -
@Kuczmil said in ListView - currentIndex cannot be set in delegate with Component.onCompleted:
I just started to wonder why it is not possible to change currentIndex inside delegate Component.onCompleted.
For me this is a non sense, you really know when ListView will create/destroy a delegate instance.
It could be because model has changed (item inserted / deleted) or View has been scrolled and item visibility changes.This is not the right place to do something like this.