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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With