3
votes

I have subclasses of QTreeView and QAbstractItemModel and currently I'm using drag-drop to move items from one parent index to another. I want to also add the ability to rearrange the order of items within the same parent index. When the user drops an item in-between two other items, I need to determine whether the drop should be into, or in-between them. I would also like to draw a dark line between the two items as the mouse is moved, to hint as to what will occur, similar to many other tree-type views (e.g. file explorers on most operating systems) as in the attached pictures:

Drag into an existing item:

drag into

Insert between two existing items:

drag in-between

Does Qt automate this part of drag/drop behavior, or do I have to manually calculate the mouse position relative to the edge of the tree item? Also, how can I draw a temporary line between two items in the QTreeView?

1

1 Answers

4
votes

I did almost the same thing some time ago, and I can think of 3 things :

Here's a very minimal example of what I was doing.

In the dragMoveEvent(), I was showing the drop indicator. This way, you will always have your drop indicator shown when you're dragging an object.

void MyTreeView::dragMoveEvent(QDragMoveEvent* event)
{
    setDropIndicatorShown(true);
    QTreeView::dragMoveEvent(event);
}

In the dropEvent(), I was managing each case, that's to say if the item I was dragging was on another item, above it, below it or on the viewport. Then, according to it, I was managing my own drop, and at the end of the event, I hid the drop indicator.

void MyTreeView::dropEvent(QDropEvent* event)
{
    bool dropOK = false;
    DropIndicatorPosition dropIndicator = dropIndicatorPosition();

    switch (dropIndicator)
    {
    case QAbstractItemView::AboveItem:
        dropOK = true;
        break;
    case QAbstractItemView::BelowItem:
        dropOK = true;
        break;
    case QAbstractItemView::OnItem:
        dropOK = false;
        break;
    case QAbstractItemView::OnViewport:
        dropOK = false;
        break;
    }
    if(dropOK)
    {
        // Here, you need to manage yourself the case of dropping an item
    }
    setDropIndicatorShown(false); // hide the drop indicator once the drop is done
}

"Bonus" : you can access the drop indicator in your own style by the PrimitiveElement PE_IndicatorItemViewItemDrop. You can see how to customize it here and here.