Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Qt: How to implement simple internal drag&drop for reordering items in QListView using a custom model

I have a QList of custom structs and i'm using custom model class (subclass of QAbstractListModel) to display those structs in 1-dimensional QListView. I have overriden the methodsrowCount, flags and data to construct a display string from the struct elements.

Now i would like to enable internal drag&drop to be able to reorder the items in the list by dragging them and dropping them between some other items, but this task seems unbeliavably complicated. What exactly do i need to override and what parameters do i need to set? I tried a lot of things, i tried

view->setDragEnabled( true );
view->setAcceptDrops( true );
view->setDragDropMode( QAbstractItemView::InternalMove );
view->setDefaultDropAction( Qt::MoveAction );

I tried

Qt::DropActions supportedDropActions() const override {
    return Qt::MoveAction;
}
Qt::ItemFlags flags( const QModelIndex & index ) const override{
    return QAbstractItemModel::flags( index ) | Qt::ItemIsDragEnabled;
}

I tried implementing insertRows and removeRows, but it still doesn't work.

I haven't found a single example of a code doing exactly that. The official documentation goes very deeply into how view/model pattern works and how to make drag&drops from external apps or from other widgets, but i don't want any of that. I only want simple internal drag&drop for manual reordering of the items in that one list view.

Can someone please help me? Or i'll get nuts from this.

EDIT: adding insertRows/removeRows implementation on request:

bool insertRows( int row, int count, const QModelIndex & parent ) override
{
    QAbstractListModel::beginInsertRows( parent, row, row + count - 1 );

    for (int i = 0; i < count; i++)
        AObjectListModel<Object>::objectList.insert( row, Object() );

    QAbstractListModel::endInsertRows();
    return true;
}

bool removeRows( int row, int count, const QModelIndex & parent ) override
{
    if (row < 0 || row + count > AObjectListModel<Object>::objectList.size())
        return false;

    QAbstractListModel::beginRemoveRows( parent, row, row + count - 1 );

    for (int i = 0; i < count; i++)
        AObjectListModel<Object>::objectList.removeAt( row );

    QAbstractListModel::endRemoveRows();
    return true;
}

objectList is QList where Object is template parameter.

like image 689
Youda008 Avatar asked Dec 04 '22 18:12

Youda008


1 Answers

In addition to the Romha's great answer, i would like to supplement few more details about how it works and what's confusing on it.

The official documentation says the QAbstractItemModel has default implementations of mimeTypes, mimeData and dropMimeData which should work for internal move and copy operations as long as you correctly implement data, setData, insertRows and removeRows.

And from certain point of view, they were right. It does work without overriding mimeData and dropMimeData, but only when your underlying data structure contains only single strings, those that are returned from data and received in setData as DisplayRole. When you have a list of compound objects (like i have) with multiple elements, only one of which is used for the DisplayRole, for example

struct Elem {
    QString name;
    int i;
    bool b;
}

QVariant data( const QModelIndex & index, int role ) const override
{
    return objectList[ index.row() ].name;
}
bool setData( const QModelIndex & index, const QVariant & value, int role ) override
{
    objectList[ index.row() ].name = value.toString();
}

then the default implementations will actually do this

QVariant data = data( oldIndex, Qt::DisplayRole );
insertRows( newIndex, 1 )
setData( newIndex, data, Qt::DisplayRole )
removeRows( oldIndex, 1 )

and therefore only correctly move the names and leave the rest of the struct as is. Which makes sense now, but the system is so complicated that i didn't realize it before.

Therefore custom mimeData and dropMimeData are required to move the whole content of the structs

like image 123
Youda008 Avatar answered Dec 06 '22 07:12

Youda008