Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Unsolved Specialize Drag&Drop behaviour in QTreeWidget

    General and Desktop
    4
    8
    321
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • S
      SimonSchroeder last edited by

      I want to specialize the drop behaviour in a QTreeWidget in order to avoid user mistakes. I do have the following structure inside my QTreeWidget:

      1. Top-level items are called Maps. There can be one or more maps.
      2. Each map can have several layers as their children.
      3. Layers have a single child (describing the objects inside the layer) which does not participate in dragging and dropping.

      The following drag&drop actions make sense within the QTreeWidget:

      1. Maps can be rearranged in their order, but are always top-level items.
      2. Layers can be rearranged in their order within a map...
      3. or moved to a different map.
      4. Layers are not allowed to become top-level items.

      By turning on drag&drop on the QTreeWidget I can do everything I want to. However, there is some unwanted behaviour as well. Based on the old non-Qt version of the software users expect they can drop layers on top of each other and it will show up below the layer it is dropped on. Currently, when dropping one layer on top of the other it becomes a child of it.

      Is there an easy way to modify the behaviour of the QTreeWidget? I have seen QAbstractItemView::DropIndicatorPosition, but there is no way to influence it as far as I can tell. We already have a derived class from QTreeWidget. dropEvent(...) is also overloaded an mostly just forwards to its parent. Maybe modifying the QDropEvent inside this method somehow influences the drop behaviour? Maybe it is also possible when dropping a layer on to a map that it shows as the first layer of the map instead of the last?

      1 Reply Last reply Reply Quote 0
      • mrjj
        mrjj Lifetime Qt Champion last edited by

        Hi
        I think you manually have to implement the dropping then.
        https://code.woboq.org/qt5/qtbase/src/widgets/itemviews/qtreewidget.cpp.html#3507

        S 1 Reply Last reply Reply Quote 2
        • S
          SimonSchroeder @mrjj last edited by

          @mrjj I hoped that I could avoid navigating through that source code. Thanks anyway!

          mrjj 1 Reply Last reply Reply Quote 0
          • mrjj
            mrjj Lifetime Qt Champion @SimonSchroeder last edited by

            @SimonSchroeder
            Well you can check the items being dropped on etc but as i recall it was not possible
            to avoid it becoming a child when dropped. But it was some years ago. Maybe i didn't try right way :)

            1 Reply Last reply Reply Quote 0
            • SGaist
              SGaist Lifetime Qt Champion last edited by

              Hi,

              It might be simpler to re-implement dropMimeData.

              You can call the base class implementation by providing the adequate parent depending on what is being dropped and the current parent.

              Interested in AI ? www.idiap.ch
              Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

              S 1 Reply Last reply Reply Quote 1
              • S
                SimonSchroeder @SGaist last edited by

                @SGaist said in Specialize Drag&Drop behaviour in QTreeWidget:

                It might be simpler to re-implement dropMimeData.

                My understanding is that dropMimeData just handles the data after it has been decided where the items should be put. It does not seem that with this function I could influence the position where the item will be inserted. Or is there something I am missing?

                B 1 Reply Last reply Reply Quote 0
                • B
                  Bonnie @SimonSchroeder last edited by Bonnie

                  @SimonSchroeder
                  You can do something like:

                  bool NewTreeWidget::dropMimeData(QTreeWidgetItem *parent, int index, const QMimeData *data, Qt::DropAction action)
                  {
                      if(QTreeWidget::dropMimeData(parent, index, data, action)) {  //call base function so you don't need to read QMimeData yourself
                          QTreeWidgetItem *newItem = parent->child(index); //get the new item inserted by the base function
                          if(some condition) //if parent is a layer and newItem is also a layer
                          {
                              newItem = parent->takeChild(index); //remove the new item from parent
                              QTreeWidgetItem *grandParent = parent->parent();
                              grandParent->insertChild(grandParent->indexOfChild(parent) + 1, newItem); //insert it to parent's parent
                          }
                          return true;
                      }
                      return false;
                  }
                  

                  [ADDED]
                  I've tested, when moving internal, dropMimeData won't be called.
                  Maybe this doesn't suit you.
                  But you can see that we can handle this when a layer item is inserted to a layer item.
                  I think of reimplement rowsInserted:

                  void NewTreeWidget::rowsInserted(const QModelIndex &parent, int start, int end)
                  {
                      QTreeWidget::rowsInserted(parent, start, end);//base function
                      QTreeWidgetItem * parentItem = itemFromIndex(parent);//get parent item
                      if(parentItem && parentItem is a layer)
                      {
                          for (int i = end; i >= start; i--) {
                              QTreeWidgetItem *newItem = parentItem->child(i);
                              if(newItem is also a layer) {
                                  newItem = parentItem->takeChild(i); //remove the new item from parent
                                  QTreeWidgetItem *grandParent = parentItem->parent();
                                  grandParent->insertChild(grandParent->indexOfChild(parentItem) + 1, newItem); //insert it to parent's parent
                              }
                          }
                      }
                  }
                  
                  S 1 Reply Last reply Reply Quote 2
                  • S
                    SimonSchroeder @Bonnie last edited by

                    Thank you, @Bonnie . Hijacking rowsInserted looks like an interesting idea. I will have a look at this approach when I have time for this again.

                    1 Reply Last reply Reply Quote 0
                    • First post
                      Last post