Sign in Join
Wiki copytemporary

LCL Drag Drop/ru: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11Руководство DoDi по Dragging (перетаскиванию), Dropping (бросанию компонента) и Docking (стыковке)
22
33Элементы управления в графическом интерфейсе можно захватить с помощью мыши, перетащить в другие места и перетащить на другие элементы управления или на рабочий стол. Когда перетаскиваемый элемент управления перемещается в другое место, операция называется [[LCL Drag Dock/ru|"docking"]]. В противном случае исходный и целевой элементы управления могут обмениваться информацией или взаимодействовать любым другим способом, что называется операцией "drag-drop" (тащи-бросай).
44
55Но все это не работает само по себе, приложение должно настроить разрешенные операции, чтобы LCL знал, что делать с каждым элементом управления в графическом интерфейсе. Сложные операции требуют дополнительного кода для управления визуальной обратной связью с пользователем приложения и того, что произойдет, когда перетаскиваемый элемент управления, наконец, будет отброшен.
66
77Для простой поддержки стыковки во всех приложениях вы можете использовать [[EasyDockingManager]] или [[Anchor Docking/ru|Anchor Docking]], что сводит к минимуму необходимые адаптации.
88
99## Drag Drop
1010
1111Это простое и изначально действие только перетаскивания. Участники:
1212
1313- Позволяющий перетаскивать себя элемент управления в графическом интерфейсе
1414- Объект перетаскивания, производный от `TDragObject`
1515- Вспомогательные функции
1616- События
1717
1818Теперь давайте посмотрим, как они работают вместе.
1919
2020### Источник перетаскивания
2121
2222Элемент управления можно сделать перетаскиваемым, установив для его свойства `DragMode` значение `dmAutomatic`. Когда пользователь нажимает левую кнопку мыши на таком элементе управления, указатель мыши превращается в индикатор перетаскивания, пока пользователь не отпустит кнопку или не отменит операцию, нажав клавишу `Escape`. Индикатор теперь следует за движениями мыши и меняет свою форму в соответствии с тем, что произойдет, когда элемент управления будет брошен в текущее место.
2323
2424Перетаскивание также можно запустить в коде, вызвав `SourceControl.BeginDrag`. В любом случае вызывается вспомогательная процедура `DragManager.DragStart` для начала перетаскивания. `DragManager` - это глобальный экземпляр, реализующий фактическое перетаскивание. Он инициализируется значением по умолчанию `TDragManagerDefault` и может быть заменен программистом (Вами). В зависимости от `DragKind` элемента управления запускается операция перетаскивания или стыковки. Исходный элемент управления запускает событие `OnStartDrag`, в котором программист может предоставить пользовательский объект перетаскивания. Если такой объект не указан, создается объект по умолчанию (`TDragControlObject` или `TDragDockObject`). Инициализируются внутренние структуры данных для визуальной обратной связи и прочего.
2525
2626### Перетаскиваемый объект (TDragObject, определенный в элементах управления модуля)
2727
2828Перетаскиваемый объект получает весь пользовательский ввод при перетаскивании. В реализации по умолчанию он реагирует вызовом либо `DragMove`, либо `DragStop`.
2929
3030Перетаскиваемый объект также отвечает за визуальную обратную связь:
3131
3232- `GetDragCursor` возвращает форму указателя мыши.
3333- `GetDragImages` возвращает список перетаскиваемых изображений, прикрепляемых к указателю мыши.
3434- `ShowDragImage` и `HideDragImage` реализуют визуальную обратную связь.
3535
3636Метод `EndDrag` уведомляет исходный элемент управления об остановке перетаскивания. В случае отмены операции он вызывает `SourceControl.DragCanceled`, за которым следует `SourceControl.DoEndDrag` в любом случае, что вызывает событие `OnEndDrag`.
3737
3838### События
3939
4040#### OnStartDrag/Dock
4141
4242Это событие происходит при запуске операции. Обработчик может предоставить настраиваемый `TDragObject` или `TDragDockObject` для операции.
4343
4444#### OnDrag/DockOver
4545
4646Это событие уведомляет возможную цель, когда над ней перетаскивают объект. Обработчик может сигнализировать о принятии или отклонении сброса компонента.
4747
4848Событие сигнализирует не только о перемещении мыши, но и о том, когда мышь входит в зону целевого элемента управления или покидает ее.
4949
5050Событие `OnDockOver` происходит на `DockSite`, а не в целевом элементе управления под указателем мыши. Обработчик может сигнализировать об отклонении сброса и может регулировать визуальную обратную связь. Событие происходит, когда мышь входит в зону элемента управления или выходит из нее, а также при каждом движении мыши.
5151
5252#### OnDrag/DockDrop
5353
5454Это событие уведомляет целевой элемент управления об окончании операции перетаскивания.
5555
5656#### OnEndDrag/Dock
5757
5858Это событие уведомляет исходный элемент управления об окончании операции перетаскивания.
5959
6060## Основные недостатки
6161
6262- Те же недостатки, что и у Delphi VCL.
6363- Нет поддержки платформы LCL (кроссплатформа) для перетаскивания компонентов из приложения LCL в любое другое приложение. Чтобы это работало, LCL необходима поддержка протокола XDND в X11, поддержка OLE Drag-n-Drop в Windows и Mac Drag Manager в OSX.
6464- Нет поддержки платформы LCL (кроссплатформа) для приема сбрасываемых компонентов внутри приложения LCL из другого приложения (рабочего стола, файлового менеджера и т.д.). Обработчик событий `OnDragDrop` всегда ожидает, что перетаскиваемый компонент(Source) будет передаваться как потомок `TObject`, что **не** будет иметь место при перетаскивании объекта из другого приложения в приложение LCL.
6565
6666# Delphi
6767
6868## Drag Drop
6969
7070Это простое и изначально действие только перетаскивания. Участники:
7171
7272- Позволяющий перетаскивать себя элемент управления в графическом интерфейсе
7373- Объект перетаскивания, производный от `TDragObject`
7474- Вспомогательные функции
7575- События
7676
7777Теперь давайте посмотрим, как они работают вместе, на основе исходной реализации Delphi для ясности. Реализация LCL несколько отличается в использовании вспомогательных объектов, подробности будут обсуждены позже.
7878
7979### Перетаскиваемый источник(объект)
8080
8181Элемент управления можно сделать перетаскиваемым, установив для его свойства `DragMode` значение `dmAutomatic`. Когда пользователь нажимает левую кнопку мыши на таком элементе управления, указатель мыши превращается в индикатор перетаскивания, пока пользователь не отпустит кнопку или не отменит операцию, нажав клавишу Escape. Индикатор теперь следует за движениями мыши и меняет свою форму в соответствии с тем, что произойдет, когда элемент управления будет брошен в текущее место.
8282
8383Перетаскивание также можно запустить программно, вызвав `Source.BeginDrag`. В любом случае вызывается вспомогательная процедура `DragInitControl` для начала перетаскивания. Исходный элемент управления вызывает событие `StartDrag`, в котором приложение может предоставить настраиваемый объект перетаскивания. Если такой объект не указан, создается объект по умолчанию. Инициализируются внутренние структуры данных для визуальной обратной связи и прочего. Затем вызывается `DragTo`, чтобы показать, что перетаскивание началось.
8484
8585### Перетаскиваемый объект (TDragObject, определенный в элементах управления модуля)
8686
8787Перетаскиваемый объект получает весь пользовательский ввод при перетаскивании. В реализации по умолчанию он реагирует вызовом либо `DragMove`, либо `DragStop`.
8888
8989Перетаскиваемый объект также отвечает за визуальную обратную связь:
9090
9191- `GetDragCursor` возвращает форму указателя мыши.
9292- `GetDragImages` возвращает список перетаскиваемых изображений, прикрепляемых к указателю мыши.
9393- `ShowDragImage` и `HideDragImage` реализуют визуальную обратную связь.
9494
9595Метод `EndDrag` уведомляет исходный элемент управления об остановке перетаскивания. В случае отмены операции он вызывает `SourceControl.DragCanceled`, за которым следует `SourceControl.DoEndDrag` в любом случае, что вызывает событие `OnEndDrag`.
9696
9797#### Модификации LCL
9898
9999Текущая реализация LCL удалила обработку ввода из объекта перетаскивания, так что приложение больше не может настраивать пользовательский интерфейс. И реализация LCL далеко не в порядке :-(
100100
101101Также объект перетаскивания игнорируется при представлении визуальной обратной связи.
102102
103103### Вспомогательные (Helper) функции
104104
105105Вспомогательные функции в первую очередь позволяют получить доступ к защищенным свойствам и методам элементов управления и других классов извне модуля `Controls`. Они также могут выполнять общие задачи и могут инкапсулировать специфические для платформы и другие детали реализации.
106106
107107#### Общие помощники
108108
109109**DragTo** (Только для Delphi, отсутствует в LCL) определяет текущую цель перетаскивания, отправляет уведомления задействованным элементам управления и управляет визуальной обратной связью. Он также обрабатывает отложенный старт операции перетаскивания, то есть может ждать, пока мышь не уйдет достаточно далеко от начальной точки.
110110
111111**DragDone** (Только для Delphi, отсутствует в LCL) завершает перетаскивание либо cбросом элемента управления в контейнер, либо отменой операции.
112112
113113**CancelDrag** (Только для Delphi, не путайте с глобальной процедурой LCL `CancelDrag`) защищен от ложных вызовов, иначе дальнейшие действия откладываются на `DragDone`.
114114
115115#### Специальные помощники
116116
117117Существует еще несколько общедоступных вспомогательных процедур, которые недокументированы в Delphi и не требуются для настраиваемых операций перетаскивания.
118118
119119**DragFindWindow** выполняет поиск целевого перетаскиваемого окна (зависит от платформы).
120120
121121**DragInit** Инициализирует внутреннее управление, такое как захват мыши, и визуальную обратную связь.
122122
123123**DragInitControl** создает объект перетаскивания, затем вызывает `DragInit`.
124124
125125**SetCaptureControl** обрабатывает захват мышью.
126126
127127### События
128128
129129#### OnStartDrag/Dock
130130
131131Это событие происходит при запуске операции. Обработчик может предоставить настраиваемый `DockObject` для операции.
132132
133133#### OnDrag/DockOver
134134
135135Это событие уведомляет возможную цель, когда над ней перетаскивают объект. Обработчик может сигнализировать о принятии или отклонении отбрасывания.
136136
137137Событие сигнализирует не только о перемещении мыши, но и о том, когда мышь входит в целевой элемент управления или покидает его.
138138
139139Событие `OnDockOver` происходит на `DockSite`, а не в целевом элементе управления под указателем мыши. Обработчик может сигнализировать об отклонении сброса и может регулировать визуальную обратную связь. Событие происходит, когда мышь входит в элемент управления или выходит из него, а также при каждом движении мыши.
140140
141141#### OnDrag/DockDrop
142142
143143Это событие уведомляет целевой элемент управления об окончании операции перетаскивания.
144144
145145#### OnEndDrag/Dock
146146
147147Это событие уведомляет исходный элемент управления об окончании операции перетаскивания.
148148
149149### События, специфические для стыковки
150150
151151(этот раздел нужно переместить в раздел стыковки)
152152
153153#### OnGetSiteInfo
154154
155155Это событие происходит во время поиска подходящего стыковочного узла. Несколько стыковочных узлов могут перекрывать друг друга, располагаться в перекрывающихся окнах или могут быть невидимыми. Каждый кандидат должен предоставить прямоугольник влияния (область захвата) и может сигнализировать об отклонении сброса элемента управления. Диспетчер перетаскивания выбирает наиболее подходящий стыковочный узел в качестве цели для последующих событий `OnDockOver`.
156156
157157#### OnUnDock
158158
159159Это событие происходит, когда перетаскиваемый элемент управления необходимо удалить со стыковочного узла.
160160
161161Непонятно, что должно произойти, если обработчик откажется отстыковывать элемент управления.
162162
163163## LCL реализация
164164
165165Реализация механизма перетаскивания в Delphi имеет много недостатков, особенно в части стыковки. До сих пор никто, кажется, не понимал деталей модели перетаскивания Delphi, большая часть кода была смоделирована после VCL и, следовательно, реализует точно такое же неправильное поведение. Этот раздел и, что более важно, страница, посвященная стыковке, должны пролить свет на модель перетаскивания в целом, а также на серьезные недостатки дизайна и реализации в реализации Delphi.
166166
167167Текущая реализация LCL использует объект DragManager и исполнителей drag/dock вместо вспомогательных функций. Причина этого критического изменения неясна - кто сможет объяснить?
168168
169169Хотя модель Delphi должна быть предоставлена ​​по соображениям совместимости, введение класса и объекта DragManager(singleton) позволяет реализовать альтернативный диспетчер перетаскивания с улучшенным поведением и позволяет приложению выбирать наиболее подходящий диспетчер.
170170
171171### DragManager
172172
173173Этот объект обрабатывает захват ввода, предоставляет вспомогательные методы и сохраняет параметры перетаскивания и стыковки.
174174
175175Сомнительно, чтобы объекту перетаскивания не разрешалось обрабатывать ввод пользователя. Это делает систему более безопасной, но не позволяет приложениям расширять пользовательский интерфейс для определенных операций перетаскивания.
176176
177177Для вспомогательных методов требуются разные вызовы, но это безвредно, если они вызываются только из компонентов LCL.
178178
179179Более важным является хранение параметров перетаскивания в объекте DragManager, которые исчезают при установке другого диспетчера перетаскивания. Это фатально в случае списка зарегистрированных стыковочных узлов :-(
180180
181181### Исполнители механизма перетаскивания
182182
183183Внутреннее использование разных классов для перетаскивания и стыковки допустимо, поскольку процедуры значительно различаются между операциями drag-drop и drag-dock. Тем не менее общие действия должны использовать общий код, например унаследован от общего базового класса.
184184
185185## Диаграмма последовательности сообщений или диаграмма последовательности UML
186186
187187Предоставлено Tom Verhoeff
188188
189189Что-то вроде линий (это для Delphi; просмотр с моноширинным шрифтом):
190190
191-`                  Main            Drag          (Optional)        Drag`\
192-`  User            Event          Source           Drag           Target`\
193-` (Mouse)          Loop           Control          Object         Control`\
194-`    =               =               =                               =`\
195-`    |               |               |                               |`\
196-`   1|--MouseDown--->|               |                               |`\
197-`    |              2#--OnMouseDown->|                               |`\
198-`    |               |              3#                               |`\
199-`    |              4#<-BeginDrag()--#                               |`\
200-`    |               #               #                               |`\
201-`    |               |               |                               |`\
202-`    |              5#--OnStartDrag->|                               |`\
203-`    |               #              6#----Create---->=               |`\
204-`    |               |               #               |               |`\
205-`   7|--MouseMove--->|               |               |               |`\
206-`    |              8#-----------------------------------OnDragOver->|`\
207-`    |               #               |               |              9#`\
208-`    |               #               |               |               #`\
209-`    |               |               |               |               |`\
210-`  10|---MouseUp---->|               |               |               |`\
211-`    |             11#-----------------------------------OnDragDrop->|`\
212-`    |               #               |               |             12#`\
213-`    |               #               |               |               #`\
214-`    |               #---OnEndDrag-->|               |               |`\
215-`    |               |             13#----Destroy--->=               |`\
216-`    |               |               |                               |`
191+``` pascal
192+ Main Drag (Optional) Drag
193+ User Event Source Drag Target
194+ (Mouse) Loop Control Object Control
195+ = = = =
196+ | | | |
197+ 1|--MouseDown--->| | |
198+ | 2#--OnMouseDown->| |
199+ | | 3# |
200+ | 4#<-BeginDrag()--# |
201+ | # # |
202+ | | | |
203+ | 5#--OnStartDrag->| |
204+ | # 6#----Create---->= |
205+ | | # | |
206+ 7|--MouseMove--->| | | |
207+ | 8#-----------------------------------OnDragOver->|
208+ | # | | 9#
209+ | # | | #
210+ | | | | |
211+ 10|---MouseUp---->| | | |
212+ | 11#-----------------------------------OnDragDrop->|
213+ | # | | 12#
214+ | # | | #
215+ | #---OnEndDrag-->| | |
216+ | | 13#----Destroy--->= |
217+ | | | |
218+```
217219
218220`DragObject` не является обязательным. Может быть несколько потенциальных целей. Цикл главного события определяет, какое событие и какому объекту отправлять, в зависимости от действия пользователя, положения курсора и текущего состояния графического интерфейса.
219221
2202224 некоторым образом запускает 5 есть задержка в зависимости от Boolean-параметра Immediate в `BeginDrag`).
221223
2222246 определяет местоположение `MouseDown` и может использовать это для принятия решения, что перетаскивается (через `GetCursorPos`, `ScreenToClient`, `MouseToCell`). Он может вызывать `SetDragImage` для визуализации перетаскиваемого объекта.
223225
224226Цепочка 7, 8, 9 может повторяться; 9 указывает на приемлемость сброса компонента на эту цель через Boolean-параметр `var Accepted`.
225227
22622812 вызывается только в том случае, если непосредственно предшествующий событию `OnDragOver` этой цели вернул `Accepted = True`; т.е. `Accepted` - предварительное условие 12.
227229
22823013 получает параметр `Target`, чтобы узнать, где был принят брошенный компонент; Если `Target=nil`, то сброс компонента был отменен пользователем (`MouseUp` в недопустимом месте), а `OnDragDrop` не было выполнено; Если `Target<>nil`, то `OnDragDrop` уже был выполнен на `Target`. Таким образом, работа, связанная с фактическим сбросом, делится на `OnDragDrop` (для действий в целевом объекте) и `OnEndDrag` (для действий в источнике). Поскольку объект перетаскивания необходимо уничтожить, независимо от того, было ли перетаскивание успешным или отмененным, лучше всего вызывать `Free` (или `Destroy`) только в обработчике `OnEndDrag`.
229231
230232В любой момент может быть активным только одно перетаскивание; Он либо завершается сбросом компоента, либо отменой сброса.
231233
232234Есть много других вещей, которые нужно включить, но это основная часть. (Можно также посмотреть [Delphi-Central](http://www.delphi-central.com/drag.aspx) или ["Implementing drag-and-drop in controls"](https://web.archive.org/web/20120422052012/http://www.podgoretsky.com/ftp/docs/Delphi/D5/dg/controls.html).)
233235
234236Надеюсь, это поможет. Tom
235237
236238## См. также
237239
238240- [[Drag and Drop sample/ru|Примеры Drag and Drop]]
239241
240242---
241243*From the Free Pascal wiki, page "LCL Drag Drop/ru", as it stood on 2021-08-19 after a change by Zoltanleo, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history