PDA

View Full Version : Strange DnD behavior from QStandardItemModel and QTreeView



Antel
18th February 2010, 18:32
Hi, i have an irritating problem id like some help with if possible.

QStandardItemModel behaves strangely when trying to disable drag and drop for the root item of a model. When disabling drag and drop for the invisibleRootItem() of the model all drop capability is removed from the rest of the tree as well, what i am wondering is whether this is the intended behavior or if i have found a bug (or perhaps i made a mistake somewhere, probably the most likely answer).

Here is the source for an example i put together to show the problem, the top tree view has the root items drag and drop unchanged and the bottom tree view has the root item with Qt::NoItemFlags set. The child of the first items in both views has drag and drop set, the parent items has only enabled and selectable set.

4304

Any ideas, any at all, will be happily received as i am currently going nuts over this. No matter what i try i cant get this dang model to work as _I_ expect it to, that is to enable drop on the children even if the root item has drop disabled.

Thank you for any help... really... any help at all. :)

wysota
19th February 2010, 11:05
The root item represents the tree itself. If you disable d&d on the root item, you effectively disallow dropping anything on the canvas of the view (I think you should still be able to drop on items in the view).

Antel
19th February 2010, 12:30
The drop on the canvas is correctly disabled when removing drop on the root item, however, the drop capability on the items in the view is also disabled when the root item drop is disabled. This is the problem i have atm, i want to drag onto items in the view, but not onto the canvas itself. But as i disable drop on the canvas i also disable drop onto the items in the view, no matter what ItemFlags i use for the items.

The example project i included has one view with a disabled root item and one view with the root item enabled. I was hoping someone could tell me if ive done something wrong by looking at the code or trying the program. Or tell me if ive found a bug for that matter.

Antel
19th February 2010, 20:31
After some more testing i have found out that it seems to be strange handling in QTreeView::dragMoveEvent() function that causes the strange behavior. If i subclass QTreeView and make my own dragMoveEvent() function i can get the view and model to accept the disabled drop on the root item (while still allowing child items to be dropped on), but not otherwise. What i had to do was check the flags myself for each item being dragged over to make the drop function correctly.

Code needed: (beware, i dont claim this is complete... this is just a quick hack to verify my theory)



void MyTreeView::dragMoveEvent(QDragMoveEvent *event) {
QModelIndex index = indexAt(event->pos());

if (index.flags() & Qt::ItemIsDropEnabled) {
event->accept();
} else {
QTreeView::dragMoveEvent(event);
}
}


The culprit was these lines in the implementation in QAbstractItemView:



...
if (index.isValid() && d->showDropIndicator) {
...
} else {
d->dropIndicatorRect = QRect();
d->dropIndicatorPosition = OnViewport;
if (d->model->flags(rootIndex()) & Qt::ItemIsDropEnabled) {
event->setDropAction(dropAction);
event->accept(); // allow dropping in empty areas
}
}
...


As i had drop indicator disabled (i draw my own) the code only checks the root items flags and thus it disables all drop indicators for the children as well. If i were to decide this would be a bug... the handling should be the same whether you want to draw your own drop indicators or not.

Below is the default implementation in QAbstractItemView:



void QAbstractItemView::dragMoveEvent(QDragMoveEvent *event)
{
Q_D(QAbstractItemView);
if (dragDropMode() == InternalMove
&& (event->source() != this || !(event->possibleActions() & Qt::MoveAction)))
return;

// ignore by default
event->ignore();

QModelIndex index = indexAt(event->pos());
if (!(event->source() == this && selectedIndexes().contains(index))
&& d->canDecode(event)) {
Qt::DropAction dropAction = (d->model->supportedDropActions() & event->proposedAction())
? event->proposedAction() : Qt::IgnoreAction;

if (index.isValid() && d->showDropIndicator) {
QRect rect = visualRect(index);
d->dropIndicatorPosition = d->position(event->pos(), rect);
switch (d->dropIndicatorPosition) {
case AboveItem:
if (d->model->flags(index.parent()) & Qt::ItemIsDropEnabled) {
d->dropIndicatorRect = QRect(rect.left(), rect.top(), rect.width(), 0);
event->setDropAction(dropAction);
event->accept();
} else {
d->dropIndicatorRect = QRect();
}
break;
case BelowItem:
if (d->model->flags(index.parent()) & Qt::ItemIsDropEnabled) {
d->dropIndicatorRect = QRect(rect.left(), rect.bottom(), rect.width(), 0);
event->setDropAction(dropAction);
event->accept();
} else {
d->dropIndicatorRect = QRect();
}
break;
case OnItem:
if (d->model->flags(index) & Qt::ItemIsDropEnabled) {
d->dropIndicatorRect = rect;
event->setDropAction(dropAction);
event->accept();
} else {
d->dropIndicatorRect = QRect();
}
break;
case OnViewport:
d->dropIndicatorRect = QRect();
break;
}
} else {
d->dropIndicatorRect = QRect();
d->dropIndicatorPosition = OnViewport;
if (d->model->flags(rootIndex()) & Qt::ItemIsDropEnabled) {
event->setDropAction(dropAction);
event->accept(); // allow dropping in empty areas
}
}
d->viewport->update();
} // can decode

if (d->shouldAutoScroll(event->pos()))
startAutoScroll();
}