Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Drag & Drop inside of a ListView



  • Hi, I'm working on a trello-like application, I have 3 ListViews and, trough my model I assign them a widget. Everything works fine, I can add, remove and edit the widgets but when I try to drag&drop only the item gets removed and the widget vanishes. Looking online it seems like the Drop method needs to be implemented again but I'm very new to Qt and MVC in general.

    MainView::MainView(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::MainView)
    {
        ui->setupUi(this);
    
        model1 = new QStandardItemModel(this);
        model2 = new QStandardItemModel(this);
        model3 = new QStandardItemModel(this);
    
    
        ui->listV1->setModel(model1); //bind view and model
        ui->listV2->setModel(model2);
        ui->listV3->setModel(model3);
    
    
    
        ui->listV1->setSelectionMode(QAbstractItemView::ExtendedSelection);
        ui->listV1->setDragEnabled(true);
        ui->listV1->setAcceptDrops(true);
        ui->listV1->setDropIndicatorShown(true);
    }
    

    This is the constructor of my main window, where the 3 lists live.
    I'm sorry if I did something wrong or asked a stupid question but as I said I'm still inexperienced with this things; anyway I'd be extremely grateful if you could explain me how to implement the Drag&Drop correctly.

    P.S. only the code for "listV1" matters since I'm testing features on one list at a time


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    What do you mean by "the widget vanishes" ?



  • Thanks for answering so fast, I mean that the item is correctly moved but it’s displayed as an empty row with no widget inside, iI can provide more code if you want, my spider sense is telling me that I really messed up somewhere with MVC implementation.
    As I said I think the “vanishing” is related to the default Drop method implementation which (correctly) moves the list item but not the linked widget


  • Lifetime Qt Champion

    Do you mean you are using setItemWidget ?



  • void MainView::on_add1_clicked()
    {
          int row =model1->rowCount();
          QStandardItem* newItem = new QStandardItem();
          Activity* act = new Activity(nullptr, this,1, newItem);
          act->resize(ui->listV1->size().width()-2, 42);
          model1->appendRow(newItem);
          model1->item(row)->setSizeHint(act->size());
          ui->listV1->setIndexWidget(model1->item(row)->index(), act);
          
    }
    

    Yes, this is the code for adding a new Activity, the steps are:

    1. get current row
      1.5) see edit
    2. create a new Activity which has null parent, a pointer to MainView (to access the models that are stored as PRIVATE attributes inside of MainView), the last argument tells us which of the 3 lists the activity belongs to
    3. resize the activity to fit the list
    4. append an empty item/row onto the model
    5. resize the newly appended item
    6. set the newly created Activity as the widget for the newly created item

    At least this was my thought process while writing this code

    EDIT: I slightly changed the code, now an Activity has even an "item" pointer attribute, this is very important to perform "setCurrentIndex" on the item linked to the Activity when the "remove" button is pressed, the logic remains the same.


  • Lifetime Qt Champion

    Why exactly do you need to use a widget for your "Activity" ?



  • An Activity needs to be a label (for example “buy milk”), a checkbox (to toggle the “completed” attribute), an “Edit” button and a “Delete” button.
    Both buttons works perfectly letting me edit the text and remove the Activity from both the listview and the model. Here’s the code for the remove button if it can help

    void MainView::deleteAct(Activity* act)
    {
        int id=act->getListId(); //to know where the Activity has to be removed
        switch(id){
    
            case(1): { 
            ui->listV1->setCurrentIndex(act->getItem()->index()); 
            //selects the item so the model can access it trough "currentIndex" 
            model1->removeRows(ui->listV1->currentIndex().row(),1);
            //the row gets removed from the model
                      }
            break;
    
           //cases for the other 2 lists
         }
            }
    

    This gets invoked by the widget by

    void Activity::on_removeButton_clicked()
    {
    
        this->point->deleteAct(this);
    
    }
    

    where "point" is a pointer to the MainView


  • Lifetime Qt Champion

    Hi
    Since Widgets cannot be copied, it's not really possible for Qt to drag-drop any attached widget. (setIndexWidget)

    You might have to handle the drag and drop yourself and recreate the Activity widget for the
    newly dropped item.



  • Thanks for answering, unfortunately I posted here because I don’t know how to do that, could you give me some clues?


  • Lifetime Qt Champion

    If it's a move, get the widget from the original cell, take it and put it back in your target.

    If it's a copy, them create a dump/restore pair of methods that allows you to copy the content of your widget one by one in your "copy".



  • I already thought about the implementation but how do I override the drop method?
    My issue is similar to this one


  • Qt Champions 2019

    @WeWon44 said in Drag & Drop inside of a ListView:

    how do I override the drop method?

    See https://doc.qt.io/qt-5/dnd.html



  • Thanks, that was very useful, I still have doubts on how to implement this correctly (mainly how to get the correct position) since I never manipulated events but I’ll give this a try and share the results in a few hours


Log in to reply