InternalMove for ListView not working when view mode is IconMode



  • I am struggling with getting a reorderable list working the way I want to. While my listview works fine as long as it is in ListMode, I cannot seem to get it working for IconMode.

    My application (https://sourceforge.net/p/tilificator/wiki/Home/) is written in Qt for Python and the code has quite a bit of tech-debt and isn't the best example to share.
    But fortunately, I found a very concise C++ example which can easily show the problem here: https://www.walletfox.com/course/qtreorderablelist.php

    This demonstrates how to display a list that can be re-ordered with drag'n'drop, with two variants of the example available: Variant1 uses a QListWidget, while Variant2 uses a QListView with a sub-classed QStringListModel.

    Both examples work just fine, allowing you to reorder the items in the list simply by calling setDragDropMode(QAbstractItemView::InternalMove). However, as soon as I add my code to switch the view mode to IconMode, this functionality disappears completely, and you are left with a widget where you can only move the items to the end, or to unoccupied slots. It is as if all the functionality to insert item before the item on the drop destination and reorder the list just gets completely omitted in IconMode.

    This is what I attempted in detail:
    For Variant1, I added the following 3 function calls to the end of CustomDialog::createListWidget:

    widget->setViewMode(QListWidget::IconMode);
    widget->setGridSize(QSize(100, 100));
    widget->setMovement(QListWidget::Snap);
    

    And this changes the widget to display the items as I want them, but no longer supports any simple reordering via drag'n'drop. When trying to drag on top of an existing item, I just get a "stop" sign indicating it's an invalid destination for dropping.

    The Variant2 of the example got me just slightly further. Again, I add the same three function calls to CustomDialog::createListModelView:

    view->setViewMode(QListView::IconMode);
    view->setGridSize(QSize(100, 100));
    view->setMovement(QListView::Snap);
    

    ... and I once again get the same behaviour.

    This time around, it appears you can tweak the behaviour by changing CustomListModel::flags to report an item as drop-enabled:

    Qt::ItemFlags CustomListModel::flags (const QModelIndex & index) const {
        Qt::ItemFlags defaultFlags = QStringListModel::flags(index);
        if (index.isValid()){
            return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
        }
    
        return defaultFlags;
    }
    

    ...but this again just leads to the same problem as I've experienced in my own application: Destination items now disappear, with the dragged item replacing them, instead of getting inserted into the list at the point of the destination item, with the subsequent items moved up.

    I am really starting to suspect this is a genuine bug in the Qt framework itself. Browsing the Qt source for QListView, I can see some suspicious code in QListView::setViewMode which re-allocates either a QListModeViewBase or a QIconModeViewBase depending on each mode. And if list mode and icon mode are handled by separate classes, it is plausible that their functionality can differ.

    In fact, I even found a bugfix done right after the 5.11 release, which sounded very much like it should had solved this particular problem:
    http://code.qt.io/cgit/qt/qtbase.git/commit/src/widgets/itemviews/qlistview.cpp?id=c699daeceb4448c4545a67ffdba27bcb3b994114

    But unfortunately, upgrading from 5.11 to 5.12 today appears to have improved nothing at all.

    It could just be that this a known discrepancy between ListMode and IconMode, and the only way to do it is to sub-class a ListModel further and implement the functionality to do the insertion manually if the current view mode is IconMode...

    But it seems surprising to me that a "view mode" should change the core functionality of a widget to such extent. Which is why I feel it must either be a bug that's gone unnoticed - or just me who's missing some obvious step to get this working.


  • Lifetime Qt Champion

    Hi
    Did you try with
    view->setMovement(QListView::Free);



  • @mrjj said in InternalMove for ListView not working in when view mode is IconMode:

    view->setMovement(QListView::Free);

    Yes, and the only difference this makes is apparently that the items can be dragged around to arbitrary positions, rather than snapping to a grid. This is not at all what I'm looking for.

    All I want is for the ListMode and IconMode to behave the same with regards to drag'n'drop, but the IconMode taking advantage of the horizontal space as well.