Specialize Drag&Drop behaviour in QTreeWidget
-
I want to specialize the drop behaviour in a QTreeWidget in order to avoid user mistakes. I do have the following structure inside my QTreeWidget:
- Top-level items are called Maps. There can be one or more maps.
- Each map can have several layers as their children.
- Layers have a single child (describing the objects inside the layer) which does not participate in dragging and dropping.
The following drag&drop actions make sense within the QTreeWidget:
- Maps can be rearranged in their order, but are always top-level items.
- Layers can be rearranged in their order within a map...
- or moved to a different map.
- Layers are not allowed to become top-level items.
By turning on drag&drop on the QTreeWidget I can do everything I want to. However, there is some unwanted behaviour as well. Based on the old non-Qt version of the software users expect they can drop layers on top of each other and it will show up below the layer it is dropped on. Currently, when dropping one layer on top of the other it becomes a child of it.
Is there an easy way to modify the behaviour of the QTreeWidget? I have seen
QAbstractItemView::DropIndicatorPosition
, but there is no way to influence it as far as I can tell. We already have a derived class from QTreeWidget.dropEvent(...)
is also overloaded an mostly just forwards to its parent. Maybe modifying theQDropEvent
inside this method somehow influences the drop behaviour? Maybe it is also possible when dropping a layer on to a map that it shows as the first layer of the map instead of the last? -
Hi
I think you manually have to implement the dropping then.
https://code.woboq.org/qt5/qtbase/src/widgets/itemviews/qtreewidget.cpp.html#3507 -
Hi
I think you manually have to implement the dropping then.
https://code.woboq.org/qt5/qtbase/src/widgets/itemviews/qtreewidget.cpp.html#3507@mrjj I hoped that I could avoid navigating through that source code. Thanks anyway!
-
@mrjj I hoped that I could avoid navigating through that source code. Thanks anyway!
@SimonSchroeder
Well you can check the items being dropped on etc but as i recall it was not possible
to avoid it becoming a child when dropped. But it was some years ago. Maybe i didn't try right way :) -
Hi,
It might be simpler to re-implement dropMimeData.
You can call the base class implementation by providing the adequate parent depending on what is being dropped and the current parent.
-
Hi,
It might be simpler to re-implement dropMimeData.
You can call the base class implementation by providing the adequate parent depending on what is being dropped and the current parent.
@SGaist said in Specialize Drag&Drop behaviour in QTreeWidget:
It might be simpler to re-implement dropMimeData.
My understanding is that
dropMimeData
just handles the data after it has been decided where the items should be put. It does not seem that with this function I could influence the position where the item will be inserted. Or is there something I am missing? -
@SGaist said in Specialize Drag&Drop behaviour in QTreeWidget:
It might be simpler to re-implement dropMimeData.
My understanding is that
dropMimeData
just handles the data after it has been decided where the items should be put. It does not seem that with this function I could influence the position where the item will be inserted. Or is there something I am missing?@SimonSchroeder
You can do something like:bool NewTreeWidget::dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action) { if(QTreeWidget::dropMimeData(parent, index, data, action)) { //call base function so you don't need to read QMimeData yourself QTreeWidgetItem *newItem = parent->child(index); //get the new item inserted by the base function if(some condition) //if parent is a layer and newItem is also a layer { newItem = parent->takeChild(index); //remove the new item from parent QTreeWidgetItem *grandParent = parent->parent(); grandParent->insertChild(grandParent->indexOfChild(parent) + 1, newItem); //insert it to parent's parent } return true; } return false; }
[ADDED]
I've tested, when moving internal,dropMimeData
won't be called.
Maybe this doesn't suit you.
But you can see that we can handle this when a layer item is inserted to a layer item.
I think of reimplementrowsInserted
:void NewTreeWidget::rowsInserted(const QModelIndex &parent, int start, int end) { QTreeWidget::rowsInserted(parent, start, end);//base function QTreeWidgetItem * parentItem = itemFromIndex(parent);//get parent item if(parentItem && parentItem is a layer) { for (int i = end; i >= start; i--) { QTreeWidgetItem *newItem = parentItem->child(i); if(newItem is also a layer) { newItem = parentItem->takeChild(i); //remove the new item from parent QTreeWidgetItem *grandParent = parentItem->parent(); grandParent->insertChild(grandParent->indexOfChild(parentItem) + 1, newItem); //insert it to parent's parent } } } }
-
@SimonSchroeder
You can do something like:bool NewTreeWidget::dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action) { if(QTreeWidget::dropMimeData(parent, index, data, action)) { //call base function so you don't need to read QMimeData yourself QTreeWidgetItem *newItem = parent->child(index); //get the new item inserted by the base function if(some condition) //if parent is a layer and newItem is also a layer { newItem = parent->takeChild(index); //remove the new item from parent QTreeWidgetItem *grandParent = parent->parent(); grandParent->insertChild(grandParent->indexOfChild(parent) + 1, newItem); //insert it to parent's parent } return true; } return false; }
[ADDED]
I've tested, when moving internal,dropMimeData
won't be called.
Maybe this doesn't suit you.
But you can see that we can handle this when a layer item is inserted to a layer item.
I think of reimplementrowsInserted
:void NewTreeWidget::rowsInserted(const QModelIndex &parent, int start, int end) { QTreeWidget::rowsInserted(parent, start, end);//base function QTreeWidgetItem * parentItem = itemFromIndex(parent);//get parent item if(parentItem && parentItem is a layer) { for (int i = end; i >= start; i--) { QTreeWidgetItem *newItem = parentItem->child(i); if(newItem is also a layer) { newItem = parentItem->takeChild(i); //remove the new item from parent QTreeWidgetItem *grandParent = parentItem->parent(); grandParent->insertChild(grandParent->indexOfChild(parentItem) + 1, newItem); //insert it to parent's parent } } } }
Thank you, @Bonnie . Hijacking
rowsInserted
looks like an interesting idea. I will have a look at this approach when I have time for this again.