c++qtdrag-and-dropqtreeviewqabstractitemview

QTreeView - how to tell if a drag/drop event is a reorder or a move between parents?


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?


Solution

  • 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.