I'm working on a training project that involves creating a Kanban board, and I'm having trouble implementing drag-n-drop for tickets both within and between categories.

In my current solution I use the CategoriesModel class, inherited from QAbstractListModel, which stores information about all categories in a particular project. It includes an object of class TicketsModel which also inherits from QAbstractListModel and is responsible for storing ticket information.

The problem is that I am not able to do the "correct" movement of the tickets within the model after visually dragging and dropping items within the DelegateModel.

In my current solution, I use the approach based on example from Qt documentation, but it doesn't provide information about moving items between two different ListView. Here are the main parts of my code that are responsible for moving the tickets:

void CategoriesModel::moveTicket(int fromCategory, int fromIndex, int toCategory, int toIndex)
{
    Ticket* tick = getTicketByIndex(fromCategory, fromIndex);
    m_categories.at(toCategory).getTickets()->insertTicketInto(tick, toIndex);
    removeTicketByIndex(fromCategory, fromIndex);

    QModelIndex fromModelIndex = createIndex(fromCategory, 0);
    QModelIndex toModelIndex   = createIndex(toCategory, 0);
    emit dataChanged(fromModelIndex, fromModelIndex);
    emit dataChanged(toModelIndex, toModelIndex);
}

void TicketsModel::moveTicketInternally(int fromIndex, int toIndex)
{
    // Invalid indexes
    if (fromIndex < 0 || fromIndex >= m_tickets.size() || toIndex < 0 || toIndex >= m_tickets.size()) { return; }

    // Start and end indexes are the same
    if (fromIndex == toIndex) { return; }

    Ticket* item = m_tickets.takeAt(fromIndex);
    insertTicketInto(item, toIndex);

    QModelIndex fromModelIndex = createIndex(fromIndex, 0);
    QModelIndex toModelIndex = createIndex(toIndex, 0);
    emit dataChanged(fromModelIndex, fromModelIndex);
    emit dataChanged(toModelIndex, toModelIndex);

}

Qml code responsible for the visualization of models:

// Ticket displaying
Item
{
    MouseArea
    {
        id: mouseArea
        property bool held: false
        anchors.fill: parent

        drag.target: held ? ticketRect : undefined
        onPressAndHold: held = true
        onReleased: held = false

        DropArea
        {
            id: dropArea
            anchors.fill: parent

            property var fromIndex
            property var toIndex
            property var fromColumn
            property var toColumn

            onEntered: (drag) => {
                fromIndex = drag.source.DelegateModel.itemsIndex;
                toIndex = mouseArea.DelegateModel.itemsIndex;
                fromColumn = drag.source.DelegateModel.groups[1];
                toColumn = mouseArea.DelegateModel.groups[1];

                if (fromColumn !== toColumn) {
                    categoriesModel.moveTicket(categoriesModel.getCategoryIndexById(fromColumn), fromIndex, categoriesModel.getCategoryIndexById(toColumn), toIndex);
                } else {
                    visualModel.items.move(fromIndex, toIndex);
                    tickets.moveTicketInternally(fromIndex, toIndex);
                }
            }
        }
    …
    }
}

// Сategories displaying
Item
{
    id: category
    property string categoryId
    property alias tickets: visualModel.model

    Rectangle
    {
        anchors.fill: parent
            DelegateModel
            {
                id: visualModel
                groups: DelegateModelGroup {
                    name: category.categoryId
                    includeByDefault: true
                }
                delegate: ModelControlTicket { header: name }
            }

            ListView
            {
                id: ticketList
                model: visualModel
            }
    }
    …
}

The main disadvantages of my approach, which are critical:

  • There needs to be an asbtract "dummyTicket" that will zone the location to which the dragged ticket will move. When implementing it, there may be problems with data synchronization in the models.
  • I use my own method moveTicketInternally to apply permutations from DelegateModel to C++ model, which in my mind is definitely not good practice.
  • The current version of the solution may occasionally have an error IndexOutOfRange for property fromCategory which will cause the program to crash. Unfortunately, I didn't manage to find the reason for the array overrun.

I would be grateful for any suggestions on how best to implement drag-n-drop functionality in this context, as well as advice on how to "properly" move tickets within a model after visually dragging and dropping items within a DelegateModel. Maybe the best solution in this scenario would be to use a completely different approach? If so, which one?

0

There are 0 best solutions below