Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Specialize Drag&Drop behaviour in QTreeWidget

Specialize Drag&Drop behaviour in QTreeWidget

Scheduled Pinned Locked Moved Unsolved General and Desktop
8 Posts 4 Posters 1.1k Views
  • 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 Offline
    S Offline
    SimonSchroeder
    wrote on last edited by
    #1

    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
    0
    • mrjjM Offline
      mrjjM Offline
      mrjj
      Lifetime Qt Champion
      wrote on last edited by
      #2

      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
      2
      • mrjjM mrjj

        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 Offline
        S Offline
        SimonSchroeder
        wrote on last edited by
        #3

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

        mrjjM 1 Reply Last reply
        0
        • S SimonSchroeder

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

          mrjjM Offline
          mrjjM Offline
          mrjj
          Lifetime Qt Champion
          wrote on last edited by
          #4

          @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
          0
          • SGaistS Offline
            SGaistS Offline
            SGaist
            Lifetime Qt Champion
            wrote on last edited by
            #5

            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
            1
            • SGaistS SGaist

              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.

              S Offline
              S Offline
              SimonSchroeder
              wrote on last edited by
              #6

              @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
              0
              • S SimonSchroeder

                @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 Offline
                B Offline
                Bonnie
                wrote on last edited by Bonnie
                #7

                @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
                2
                • B 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 Offline
                  S Offline
                  SimonSchroeder
                  wrote on last edited by
                  #8

                  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
                  0

                  • Login

                  • Login or register to search.
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved