Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. ListView - currentIndex cannot be set in delegate with Component.onCompleted
Forum Update on Monday, May 27th 2025

ListView - currentIndex cannot be set in delegate with Component.onCompleted

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
4 Posts 2 Posters 551 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • K Offline
    K Offline
    Kuczmil
    wrote on 6 Aug 2020, 14:21 last edited by
    #1

    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 to 2. 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 from onCompleted part, even with the interval set to 0, also allows for the currentIndex 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.cpp

    FxViewItem *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 calling onCompleted inside the delegate does not actually mean that the creation is fully completed.

    Generally, the questions:

    1. Am I correct that inRequest variable prevents from changing currentIndex in the way shown on the first code snippet in this post?
    2. Isn't that a bug?
    K 1 Reply Last reply 6 Aug 2020, 14:56
    0
    • K Kuczmil
      6 Aug 2020, 14:21

      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 to 2. 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 from onCompleted part, even with the interval set to 0, also allows for the currentIndex 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.cpp

      FxViewItem *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 calling onCompleted inside the delegate does not actually mean that the creation is fully completed.

      Generally, the questions:

      1. Am I correct that inRequest variable prevents from changing currentIndex in the way shown on the first code snippet in this post?
      2. Isn't that a bug?
      K Offline
      K Offline
      KroMignon
      wrote on 6 Aug 2020, 14:56 last edited by
      #2

      @Kuczmil I don't think I understand what you want to achieve with this code?

      Why do you want to set ListView.currentIndex in delegate Component.onCompleted.
      This looks very strange to me, can you explain what is the goal of this?

      It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

      1 Reply Last reply
      0
      • K Offline
        K Offline
        Kuczmil
        wrote on 6 Aug 2020, 15:06 last edited by
        #3

        @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_63252698

        I just started to wonder why it is not possible to change currentIndex inside delegate Component.onCompleted. What is the reason? The code I provided is as simple as possible just to expose the issue.

        K 1 Reply Last reply 6 Aug 2020, 16:01
        0
        • K Kuczmil
          6 Aug 2020, 15:06

          @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_63252698

          I just started to wonder why it is not possible to change currentIndex inside delegate Component.onCompleted. What is the reason? The code I provided is as simple as possible just to expose the issue.

          K Offline
          K Offline
          KroMignon
          wrote on 6 Aug 2020, 16:01 last edited by
          #4

          @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.

          It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

          1 Reply Last reply
          0

          1/4

          6 Aug 2020, 14:21

          • Login

          • Login or register to search.
          1 out of 4
          • First post
            1/4
            Last post
          0
          • Categories
          • Recent
          • Tags
          • Popular
          • Users
          • Groups
          • Search
          • Get Qt Extensions
          • Unsolved