Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: Igors от Июнь 20, 2017, 17:17



Название: DnD в дереве
Отправлено: Igors от Июнь 20, 2017, 17:17
Добрый день

Есть QTreeWidget, юзер выбрал неск айтемов и тянет их. Допустим мыша оказалась сейчас между item1 и item2. Как определить можно ли туда вставить выбранное (и значит нарисовать намек - линию разделителя) или нет? Собсно требуется точно такое же поведение как у Xcode когда дропаем файлы в проект

Спасибо


Название: Re: DnD в дереве
Отправлено: Авварон от Июнь 20, 2017, 17:26
Код:
QAbstractItemModel::canDropMimeData


Название: Re: DnD в дереве
Отправлено: Igors от Июнь 21, 2017, 12:31
Код:
QAbstractItemModel::canDropMimeData
Ага
Цитировать
Лучшие друзья девушек это бриллианты
:)
Но и в этом случае его надо писать. Упростим задачу - нет никаких mime, нас интересует только move нодов, драг происходит в одном дереве, selection известен (т.е. строго говоря это не DnD). Если точка вставки известна, то дальше все очевидно (take + insert)   


Название: Re: DnD в дереве
Отправлено: Авварон от Июнь 21, 2017, 15:16
DnD во вью уже реализован и вызывает методы модели. Написать модель много раз проще, чем повторно перекрывать DnD.
Он не так прост, я когда-то курил этот код.
Лучше через модель. Можно даже через StandardItemModel - почти как тривиджет, только модель.
Upd: ну и при реализации DnD руками от MimeData никуда не уйти:)


Название: Re: DnD в дереве
Отправлено: Авварон от Июнь 21, 2017, 15:22
Ну в простейшем случае, есть флажки у вьюхи, которые разрешают/запрещают мув айтемов.
Соответственно, никакой логики по запрету (верно я понял, что нужно именно это?) написать не получится.
Если же не требуется никакой логики, то юзайте флажки, мув должен автоматом работать.


Название: Re: DnD в дереве
Отправлено: gil9red от Июнь 21, 2017, 15:25
У меня в старой проге было дерево с папками и элементами в них, ну и работало перетаскивание мышкой в папки. Можно было сразу кучу так брать и бросать в папку, правда код не мой и давно это было...

Та модель дерева: .h (https://github.com/gil9red/NotesManager/blob/master/NavigationPanel/hierarchymodel.h) и .cpp (https://github.com/gil9red/NotesManager/blob/master/NavigationPanel/hierarchymodel.cpp)


Название: Re: DnD в дереве
Отправлено: Igors от Июнь 21, 2017, 15:59
Что касается "оформления" (отслеживания мыши и.т.п.) - с этим проблем нет.  К слову: влезать во все эти mime,  drag/drop event'ы есть смысл если DnD планируется между приложениями (редко) или хотя бы между разными виджетами/окнами одного приложения. Если же, как в данном случае, все в рамках одного дерева - проще самому. Ну в самом деле, чего светить dragImage за пределами виджета если никаких дропов там делать не собирались? Да и имедж тот у них совсем колхозный

Лучше через модель.
Ну так об этом и вопрос,
..мув должен автоматом работать.
Простой пример иерархии
Цитировать
Item0
   Item1 (selected, dragged)
            Item1_1
                           <---- мышь здесь
            Item1_2
Юзер не может вставить Item1 между его чайлдами Item1_1 и Item1_2. Однако это лишь простейший случай, есть и много других - вот о чем я хотел поговорить


Название: Re: DnD в дереве
Отправлено: Авварон от Июнь 21, 2017, 16:32
Что касается "оформления" (отслеживания мыши и.т.п.) - с этим проблем нет.  К слову: влезать во все эти mime,  drag/drop event'ы есть смысл если DnD планируется между приложениями (редко) или хотя бы между разными виджетами/окнами одного приложения. Если же, как в данном случае, все в рамках одного дерева - проще самому. Ну в самом деле, чего светить dragImage за пределами виджета если никаких дропов там делать не собирались? Да и имедж тот у них совсем колхозный

Лучше через модель.
Ну так об этом и вопрос,
..мув должен автоматом работать.
Простой пример иерархии
Цитировать
Item0
   Item1 (selected, dragged)
            Item1_1
                           <---- мышь здесь
            Item1_2
Юзер не может вставить Item1 между его чайлдами Item1_1 и Item1_2. Однако это лишь простейший случай, есть и много других - вот о чем я хотел поговорить

Что значит "проще самому?" У вас в драг эвент приходить MimeData и от неё никуда не деться. Что вы в неё положите (ид айтема или тупо row/column/parent) это ваше дело. По сути, это просто контейнер и вы сейчас звучите "зачем в QMap лезть разбираться, можно же самому, руками"


Название: Re: DnD в дереве
Отправлено: Igors от Июнь 21, 2017, 16:47
По сути, это просто контейнер и вы сейчас звучите "зачем в QMap лезть разбираться, можно же самому, руками"
Не надо все понимать слишком буквально. Написать красно-черное дерево руками можно, но это намного сложнее чем прочитать QMap или std::map

Что значит "проще самому?" У вас в драг эвент приходить MimeData и от неё никуда не деться. Что вы в неё положите (ид айтема или тупо row/column/parent) это ваше дело.
А здесь ситуация другая, резонов задействовать Qt DnD нет, можно mouseMove или еще лучше вторичный цикл событий (как я и делаю). Но все это отношения у теме не имеет

- есть выборка (selection) в дереве (неважно как о ней узнали, через мимю или нет)
- есть текущая точка вставки от мыши

Она валидна или нет?


Название: Re: DnD в дереве
Отправлено: Авварон от Июнь 21, 2017, 16:49
https://github.com/qt/qtbase/blob/dev/src/widgets/itemviews/qabstractitemview.cpp#L3674
https://github.com/qt/qtbase/blob/dev/src/widgets/itemviews/qabstractitemview.cpp#L2107

Она валидна или нет?


https://github.com/qt/qtbase/blob/dev/src/widgets/itemviews/qabstractitemview.cpp#L2145
https://github.com/qt/qtbase/blob/dev/src/widgets/itemviews/qabstractitemview.cpp#L2193


Название: Re: DnD в дереве
Отправлено: Igors от Июнь 21, 2017, 17:06
https://github.com/qt/qtbase/blob/dev/src/widgets/itemviews/qabstractitemview.cpp#L2145
https://github.com/qt/qtbase/blob/dev/src/widgets/itemviews/qabstractitemview.cpp#L2193
Изучение исходников полезно до определенного предела  :) Вот та же иерархия что и выше, но мышь сместилась
Цитировать
Item0
   Item1 (selected, dragged)
            Item1_1
            Item1_2
                           <---- мышь здесь
И, как ни странно, Item1 сюда мувить можно :)


Название: Re: DnD в дереве
Отправлено: Авварон от Июнь 21, 2017, 17:09
И чо? Я вам привел код, который это обрабатывает, нет?


Название: Re: DnD в дереве
Отправлено: Igors от Июнь 22, 2017, 08:38
И чо? Я вам привел код, который это обрабатывает, нет?
К сожалению не обрабатывает. И еще: если Вы заметили то в одно и то же место можно вставить неск способами, в Qt этого ф-ционала нет


Название: Re: DnD в дереве
Отправлено: Авварон от Июнь 22, 2017, 11:33
Ок, покурил ещё, действительно, случай, когда мышь внизу обрабатывается так: вставка идет в рут.
Насчет нескольких способов - можно либо между айтемами воткнуть, либо на сам айтем дропнуть. Или ещё какие способы?


Название: Re: DnD в дереве
Отправлено: Igors от Июнь 22, 2017, 11:41
Вроде все получилось. Псевдокод
Код
C++ (Qt)
// mouse is inbetween item1 & item2 (onr can be null)
bool CanInsertHere(  QTreeWidgetItem *& item1, QTreeWidgetItem *& item2 )
{
 while (item2 && item2->isSelected())
   item2 = itemBelow(item2);
 
 if (item2 && item2->HasSelectedParent()) return false;   // can't insert here
 
 while (item1 && (item1->isSelected() || item1->HasSelectedParent()))
   item1 = itemAbove(item1);
 
 return item1 || item2;
}
 
Правда это еще далеко не все  :)


Название: Re: DnD в дереве
Отправлено: Авварон от Июнь 22, 2017, 11:51
Непонятно. Вставка только между выбранными айтемами? В поставновке задачи это не сказано:) (как обычно)


Название: Re: DnD в дереве
Отправлено: Igors от Июнь 22, 2017, 13:35
Непонятно. Вставка только между выбранными айтемами? В поставновке задачи это не сказано:) (как обычно)
А постановка и не должна перечислять все что не запрещено. Код выше по существу находит ту пару айтемов что была бы в дереве если удалить все selected.

Дальше мы имеем N слотов для вставки (хотя бы 1 всегда есть). Пример
Код
C++ (Qt)
int maxDepth = item1->Depth() + (item1->IsOpenedFolder() ? 1 ; 0);
int minDepth = item2->Depth();
 
Напр depth оказались 4 и 2, значит можно вставить сразу после item1 (верхнего над мышей), на том же уровне или в его чайлдов, можно в конец его парента или в конец его парент-парента, во всех случаях вставляемое появится там где рисовалась линия вставки. По существу все решено, осталось только показать юзверю где текущий слот. Ну то уже чисто подробности рисования и отслеживания мыши