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. [Solved] Automatically scroll list to end / bottom
QtWS25 Last Chance

[Solved] Automatically scroll list to end / bottom

Scheduled Pinned Locked Moved QML and Qt Quick
qmllistviewscrollviewflickabstractlistqabstractlistmo
10 Posts 5 Posters 27.4k 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.
  • TobiasT Offline
    TobiasT Offline
    Tobias
    wrote on last edited by Tobias
    #1

    Hi,
    I think it is a common use case that a list should always show the last element as long as the user never scrolls manually to a another position. Especially console like thinks or log outputs behave like this.
    The newest entry is always at the bottom of an upwards scrolling list. And if a new entry is added the list scrolls up just enough to show the new entry. As soon as the user scrolls manually the auto- scroll stops. And if the user scrolls back to the bottom auto-scroll is re-enabled.

    Currently my setup is the following:

    • My data is repressed as QAbstractListModel Subclass.
      • If new entries are added my model calls beginInsertRows and endInsertRows. Entries are only added at the end of the list.
      • If entries are removed beginRemoveRows and endRemoveRows are called. Entries are only removed from the front of the list.
      • The model can change quite quickly like adding and removing ~100 elements in a second. This can happen rarely but exact this brakes my current solution most the times.
    • The model is displayed in a ListView which is included in a ScrollView. (

    What I'm trying:
    In onContentYChanged (my indication if the user changed the position) I check the atYEnd flag and remember it in an own property scrollToEnd (form my observations it is not rally really able to only track this flag)
    As I can't find any other indication that the entries of the model have changed (added/removed) I use the onCountChanged handler to invoke ListView::positionViewAtEnd if my scrollToEnd property is true .
    This works kind of but also brakes a lot of times :(

    I would also considerer to trigger the scrolling from my QAbstractListModel, maybe someone found a working solution on this path.

    From my expectation I'm not the first how tries to achieve this,
    so any pointer or hint is welcome.

    Cheers
    Tobi

    O p3c0P 2 Replies Last reply
    0
    • TobiasT Tobias

      Hi,
      I think it is a common use case that a list should always show the last element as long as the user never scrolls manually to a another position. Especially console like thinks or log outputs behave like this.
      The newest entry is always at the bottom of an upwards scrolling list. And if a new entry is added the list scrolls up just enough to show the new entry. As soon as the user scrolls manually the auto- scroll stops. And if the user scrolls back to the bottom auto-scroll is re-enabled.

      Currently my setup is the following:

      • My data is repressed as QAbstractListModel Subclass.
        • If new entries are added my model calls beginInsertRows and endInsertRows. Entries are only added at the end of the list.
        • If entries are removed beginRemoveRows and endRemoveRows are called. Entries are only removed from the front of the list.
        • The model can change quite quickly like adding and removing ~100 elements in a second. This can happen rarely but exact this brakes my current solution most the times.
      • The model is displayed in a ListView which is included in a ScrollView. (

      What I'm trying:
      In onContentYChanged (my indication if the user changed the position) I check the atYEnd flag and remember it in an own property scrollToEnd (form my observations it is not rally really able to only track this flag)
      As I can't find any other indication that the entries of the model have changed (added/removed) I use the onCountChanged handler to invoke ListView::positionViewAtEnd if my scrollToEnd property is true .
      This works kind of but also brakes a lot of times :(

      I would also considerer to trigger the scrolling from my QAbstractListModel, maybe someone found a working solution on this path.

      From my expectation I'm not the first how tries to achieve this,
      so any pointer or hint is welcome.

      Cheers
      Tobi

      O Offline
      O Offline
      onek24
      wrote on last edited by
      #2

      @Tobias From the docs i take that you should call positionViewAtEnd only once and after your component has been created.

      1 Reply Last reply
      0
      • TobiasT Tobias

        Hi,
        I think it is a common use case that a list should always show the last element as long as the user never scrolls manually to a another position. Especially console like thinks or log outputs behave like this.
        The newest entry is always at the bottom of an upwards scrolling list. And if a new entry is added the list scrolls up just enough to show the new entry. As soon as the user scrolls manually the auto- scroll stops. And if the user scrolls back to the bottom auto-scroll is re-enabled.

        Currently my setup is the following:

        • My data is repressed as QAbstractListModel Subclass.
          • If new entries are added my model calls beginInsertRows and endInsertRows. Entries are only added at the end of the list.
          • If entries are removed beginRemoveRows and endRemoveRows are called. Entries are only removed from the front of the list.
          • The model can change quite quickly like adding and removing ~100 elements in a second. This can happen rarely but exact this brakes my current solution most the times.
        • The model is displayed in a ListView which is included in a ScrollView. (

        What I'm trying:
        In onContentYChanged (my indication if the user changed the position) I check the atYEnd flag and remember it in an own property scrollToEnd (form my observations it is not rally really able to only track this flag)
        As I can't find any other indication that the entries of the model have changed (added/removed) I use the onCountChanged handler to invoke ListView::positionViewAtEnd if my scrollToEnd property is true .
        This works kind of but also brakes a lot of times :(

        I would also considerer to trigger the scrolling from my QAbstractListModel, maybe someone found a working solution on this path.

        From my expectation I'm not the first how tries to achieve this,
        so any pointer or hint is welcome.

        Cheers
        Tobi

        p3c0P Offline
        p3c0P Offline
        p3c0
        Moderators
        wrote on last edited by
        #3

        Hi @Tobias,
        AFAIK ListView::positionViewAtEnd is the only way to do that (correct me if I'm wrong). May be calling that method onCountChanged is not a good idea as the count will always keep on changing as you go on adding it.

        From C++ side QML methods can be invoked using QMetaObject::invokeMethod. Call it when all the new items get added into the model. To make this work you will need to find the ListView by objectName first on C++ side using findChild. Later you can pass the object to QAbstractListModel sub class and then call that positionViewAtEnd method.
        To find ListView:

        QQuickView view;
        view.setSource(QUrl(QStringLiteral("qrc:/main.qml")));
        QQuickItem *itm = view.rootObject()->findChild<QQuickItem*>("mylistview"); //ListView objectName
        view.show();
        if(itm) {
            qDebug() << "ListView Found";
        }
        
        QMetaObject::invokeMethod(itm, "positionViewAtEnd"); //invoke that function
        

        157

        1 Reply Last reply
        0
        • TobiasT Offline
          TobiasT Offline
          Tobias
          wrote on last edited by
          #4

          Hi
          @onek24 Could be red this way you are right but from other use cases I've found that you can (might be shouldn't) call it at any point after the Component.onCompleted was called
          Additional if found (but can not find it again) a stackoverflow entry which recommended to set the currentItem to the last item of the list if you want to use positionViewAtEnd.

          @p3c0 Since I posted I tried doing what you recomaned but with a C++-signal and a QML-"slot" instead of a direct invoke. Which at least gives me the possibility to alway scroll down triggered form C++, this is nice but the same effekt was already possible from just calling positionViewAtEnd() in onCountChanged :(
          I still cant find a way to disable and re-enable the auto scrolling as a user.

          Do you think this feature is common enough for a feature request to Qt or is this really a rare use case?

          Cheers and thanks for the input
          Tobi

          p3c0P 1 Reply Last reply
          0
          • TobiasT Tobias

            Hi
            @onek24 Could be red this way you are right but from other use cases I've found that you can (might be shouldn't) call it at any point after the Component.onCompleted was called
            Additional if found (but can not find it again) a stackoverflow entry which recommended to set the currentItem to the last item of the list if you want to use positionViewAtEnd.

            @p3c0 Since I posted I tried doing what you recomaned but with a C++-signal and a QML-"slot" instead of a direct invoke. Which at least gives me the possibility to alway scroll down triggered form C++, this is nice but the same effekt was already possible from just calling positionViewAtEnd() in onCountChanged :(
            I still cant find a way to disable and re-enable the auto scrolling as a user.

            Do you think this feature is common enough for a feature request to Qt or is this really a rare use case?

            Cheers and thanks for the input
            Tobi

            p3c0P Offline
            p3c0P Offline
            p3c0
            Moderators
            wrote on last edited by
            #5

            @Tobias Setting the currentIndex of ListView should also work. Set the currentIndex to ListView.count-1.

            onCountChanged: {
                        listview.currentIndex = count - 1
            }
            

            Have a try requesting it but I guess they would suggest the other two ways to do it.

            157

            1 Reply Last reply
            0
            • TobiasT Offline
              TobiasT Offline
              Tobias
              wrote on last edited by
              #6

              Hi,
              I found that onCountChanged is not quit called at the right time, at least it feels like this. The auto-scroll works fine (mostly) but the scrollbar seams to lack behind the change which looks not nice.

              My current solution forwards the atYEnd property to my C++-Controller.
              And the code that adds a new entry looks like this.

                  void TController::addEntry(const TEntry &Entry)
                  {     
                    bool OldAutoScrollToEnd = m_AutoScrollToEnd;
                    beginInsertRows(QModelIndex(), rowCount(), rowCount());
                    m_Storage.append(Entry);
                    endInsertRows();
              
                    if (OldAutoScrollToEnd) {
                      emit scrollViewToEnd();
                    }
                  }
              

              In QML the scrollViewToEnd signal evokes

               function scrollViewToEnd() {
                  var newIndex = listView.count - 1; // last index
                  listView.positionViewAtEnd();
                  listView.currentIndex = newIndex;
               } //function scrollToEnd()
              

              So that is all fine, until I resize my ListView :D
              If I find a solution to this I'll post it here

              Cheers
              Tobi

              1 Reply Last reply
              0
              • TobiasT Offline
                TobiasT Offline
                Tobias
                wrote on last edited by
                #7

                Ok now it seams to work.
                Code looks not that complicated, but I'm also not sure if the execution order of the onPropyrtChanged handlers might end up being an issue if they change ...

                property bool lastAtYEnd: true
                onAtYEndChanged: {
                  if(controller!= null) {
                    controller.setAutoScrollToEnd(atYEnd); 
                  }
                  lastAtYEnd = atYEnd;
                } //onAtYEndChanged
                
                onHeightChanged: {
                  if(lastAtYEnd) {
                    scrollViewToEnd();
                  }
                } //onHeightChanged
                

                I'm still not completely happy with the solution as it looks a bit fragile but time is short so until our test team does not find a bug I'll stick to this solution so far...

                Cheers
                Tobi

                1 Reply Last reply
                0
                • TobiasT Offline
                  TobiasT Offline
                  Tobias
                  wrote on last edited by
                  #8

                  Marked as solved.
                  Till now no further problems occurred with the solution mentioned.

                  1 Reply Last reply
                  0
                  • S Offline
                    S Offline
                    SiliconKiwi
                    wrote on last edited by
                    #9

                    This thread is a bit old but....

                    An easier solution that worked for me is to use callLater to eliminate the problem.

                    onCountChanged: {
                    	if (moveToEnd) {
                    		Qt.callLater( listview.positionViewAtEnd )
                    	}
                    }
                    
                    A 1 Reply Last reply
                    6
                    • S SiliconKiwi

                      This thread is a bit old but....

                      An easier solution that worked for me is to use callLater to eliminate the problem.

                      onCountChanged: {
                      	if (moveToEnd) {
                      		Qt.callLater( listview.positionViewAtEnd )
                      	}
                      }
                      
                      A Offline
                      A Offline
                      Alegriabaile
                      wrote on last edited by
                      #10

                      @SiliconKiwi helps, thanks.

                      1 Reply Last reply
                      0

                      • Login

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