Sign in Join
Wiki copytemporary

LCL Drag Drop/ru: what changed

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

OldNewLine
1-<h2>
2-
31Руководство DoDi по Dragging (перетаскиванию), Dropping (бросанию компонента) и Docking (стыковке)
4-
5-</h2>
62
73Элементы управления в графическом интерфейсе можно захватить с помощью мыши, перетащить в другие места и перетащить на другие элементы управления или на рабочий стол. Когда перетаскиваемый элемент управления перемещается в другое место, операция называется [[LCL Drag Dock/ru|"docking"]]. В противном случае исходный и целевой элементы управления могут обмениваться информацией или взаимодействовать любым другим способом, что называется операцией "drag-drop" (тащи-бросай).
84
95Но все это не работает само по себе, приложение должно настроить разрешенные операции, чтобы LCL знал, что делать с каждым элементом управления в графическом интерфейсе. Сложные операции требуют дополнительного кода для управления визуальной обратной связью с пользователем приложения и того, что произойдет, когда перетаскиваемый элемент управления, наконец, будет отброшен.
106
117Для простой поддержки стыковки во всех приложениях вы можете использовать [[EasyDockingManager]] или [[Anchor Docking/ru|Anchor Docking]], что сводит к минимуму необходимые адаптации.
128
139## Drag Drop
1410
1511Это простое и изначально действие только перетаскивания. Участники:
1612
1713- Позволяющий перетаскивать себя элемент управления в графическом интерфейсе
1814- Объект перетаскивания, производный от `TDragObject`
1915- Вспомогательные функции
2016- События
2117
2218Теперь давайте посмотрим, как они работают вместе.
2319
2420### Источник перетаскивания
2521
2622Элемент управления можно сделать перетаскиваемым, установив для его свойства `DragMode` значение `dmAutomatic`. Когда пользователь нажимает левую кнопку мыши на таком элементе управления, указатель мыши превращается в индикатор перетаскивания, пока пользователь не отпустит кнопку или не отменит операцию, нажав клавишу `Escape`. Индикатор теперь следует за движениями мыши и меняет свою форму в соответствии с тем, что произойдет, когда элемент управления будет брошен в текущее место.
2723
2824Перетаскивание также можно запустить в коде, вызвав `SourceControl.BeginDrag`. В любом случае вызывается вспомогательная процедура `DragManager.DragStart` для начала перетаскивания. `DragManager` - это глобальный экземпляр, реализующий фактическое перетаскивание. Он инициализируется значением по умолчанию `TDragManagerDefault` и может быть заменен программистом (Вами). В зависимости от `DragKind` элемента управления запускается операция перетаскивания или стыковки. Исходный элемент управления запускает событие `OnStartDrag`, в котором программист может предоставить пользовательский объект перетаскивания. Если такой объект не указан, создается объект по умолчанию (`TDragControlObject` или `TDragDockObject`). Инициализируются внутренние структуры данных для визуальной обратной связи и прочего.
2925
3026### Перетаскиваемый объект (TDragObject, определенный в элементах управления модуля)
3127
3228Перетаскиваемый объект получает весь пользовательский ввод при перетаскивании. В реализации по умолчанию он реагирует вызовом либо `DragMove`, либо `DragStop`.
3329
3430Перетаскиваемый объект также отвечает за визуальную обратную связь:
3531
3632- `GetDragCursor` возвращает форму указателя мыши.
3733- `GetDragImages` возвращает список перетаскиваемых изображений, прикрепляемых к указателю мыши.
3834- `ShowDragImage` и `HideDragImage` реализуют визуальную обратную связь.
3935
4036Метод `EndDrag` уведомляет исходный элемент управления об остановке перетаскивания. В случае отмены операции он вызывает `SourceControl.DragCanceled`, за которым следует `SourceControl.DoEndDrag` в любом случае, что вызывает событие `OnEndDrag`.
4137
4238### События
4339
4440#### OnStartDrag/Dock
4541
4642Это событие происходит при запуске операции. Обработчик может предоставить настраиваемый `TDragObject` или `TDragDockObject` для операции.
4743
4844#### OnDrag/DockOver
4945
5046Это событие уведомляет возможную цель, когда над ней перетаскивают объект. Обработчик может сигнализировать о принятии или отклонении сброса компонента.
5147
5248Событие сигнализирует не только о перемещении мыши, но и о том, когда мышь входит в зону целевого элемента управления или покидает ее.
5349
5450Событие `OnDockOver` происходит на `DockSite`, а не в целевом элементе управления под указателем мыши. Обработчик может сигнализировать об отклонении сброса и может регулировать визуальную обратную связь. Событие происходит, когда мышь входит в зону элемента управления или выходит из нее, а также при каждом движении мыши.
5551
5652#### OnDrag/DockDrop
5753
5854Это событие уведомляет целевой элемент управления об окончании операции перетаскивания.
5955
6056#### OnEndDrag/Dock
6157
6258Это событие уведомляет исходный элемент управления об окончании операции перетаскивания.
6359
6460## Основные недостатки
6561
6662- Те же недостатки, что и у Delphi VCL.
6763- Нет поддержки платформы LCL (кроссплатформа) для перетаскивания компонентов из приложения LCL в любое другое приложение. Чтобы это работало, LCL необходима поддержка протокола XDND в X11, поддержка OLE Drag-n-Drop в Windows и Mac Drag Manager в OSX.
6864- Нет поддержки платформы LCL (кроссплатформа) для приема сбрасываемых компонентов внутри приложения LCL из другого приложения (рабочего стола, файлового менеджера и т.д.). Обработчик событий `OnDragDrop` всегда ожидает, что перетаскиваемый компонент(Source) будет передаваться как потомок `TObject`, что **не** будет иметь место при перетаскивании объекта из другого приложения в приложение LCL.
6965
7066# Delphi
7167
7268## Drag Drop
7369
7470Это простое и изначально действие только перетаскивания. Участники:
7571
7672- Позволяющий перетаскивать себя элемент управления в графическом интерфейсе
7773- Объект перетаскивания, производный от `TDragObject`
7874- Вспомогательные функции
7975- События
8076
8177Теперь давайте посмотрим, как они работают вместе, на основе исходной реализации Delphi для ясности. Реализация LCL несколько отличается в использовании вспомогательных объектов, подробности будут обсуждены позже.
8278
8379### Перетаскиваемый источник(объект)
8480
8581Элемент управления можно сделать перетаскиваемым, установив для его свойства `DragMode` значение `dmAutomatic`. Когда пользователь нажимает левую кнопку мыши на таком элементе управления, указатель мыши превращается в индикатор перетаскивания, пока пользователь не отпустит кнопку или не отменит операцию, нажав клавишу Escape. Индикатор теперь следует за движениями мыши и меняет свою форму в соответствии с тем, что произойдет, когда элемент управления будет брошен в текущее место.
8682
8783Перетаскивание также можно запустить программно, вызвав `Source.BeginDrag`. В любом случае вызывается вспомогательная процедура `DragInitControl` для начала перетаскивания. Исходный элемент управления вызывает событие `StartDrag`, в котором приложение может предоставить настраиваемый объект перетаскивания. Если такой объект не указан, создается объект по умолчанию. Инициализируются внутренние структуры данных для визуальной обратной связи и прочего. Затем вызывается `DragTo`, чтобы показать, что перетаскивание началось.
8884
8985### Перетаскиваемый объект (TDragObject, определенный в элементах управления модуля)
9086
9187Перетаскиваемый объект получает весь пользовательский ввод при перетаскивании. В реализации по умолчанию он реагирует вызовом либо `DragMove`, либо `DragStop`.
9288
9389Перетаскиваемый объект также отвечает за визуальную обратную связь:
9490
9591- `GetDragCursor` возвращает форму указателя мыши.
9692- `GetDragImages` возвращает список перетаскиваемых изображений, прикрепляемых к указателю мыши.
9793- `ShowDragImage` и `HideDragImage` реализуют визуальную обратную связь.
9894
9995Метод `EndDrag` уведомляет исходный элемент управления об остановке перетаскивания. В случае отмены операции он вызывает `SourceControl.DragCanceled`, за которым следует `SourceControl.DoEndDrag` в любом случае, что вызывает событие `OnEndDrag`.
10096
10197#### Модификации LCL
10298
10399Текущая реализация LCL удалила обработку ввода из объекта перетаскивания, так что приложение больше не может настраивать пользовательский интерфейс. И реализация LCL далеко не в порядке :-(
104100
105101Также объект перетаскивания игнорируется при представлении визуальной обратной связи.
106102
107103### Вспомогательные (Helper) функции
108104
109105Вспомогательные функции в первую очередь позволяют получить доступ к защищенным свойствам и методам элементов управления и других классов извне модуля `Controls`. Они также могут выполнять общие задачи и могут инкапсулировать специфические для платформы и другие детали реализации.
110106
111107#### Общие помощники
112108
113109**DragTo** (Только для Delphi, отсутствует в LCL) определяет текущую цель перетаскивания, отправляет уведомления задействованным элементам управления и управляет визуальной обратной связью. Он также обрабатывает отложенный старт операции перетаскивания, то есть может ждать, пока мышь не уйдет достаточно далеко от начальной точки.
114110
115111**DragDone** (Только для Delphi, отсутствует в LCL) завершает перетаскивание либо cбросом элемента управления в контейнер, либо отменой операции.
116112
117113**CancelDrag** (Только для Delphi, не путайте с глобальной процедурой LCL `CancelDrag`) защищен от ложных вызовов, иначе дальнейшие действия откладываются на `DragDone`.
118114
119115#### Специальные помощники
120116
121117Существует еще несколько общедоступных вспомогательных процедур, которые недокументированы в Delphi и не требуются для настраиваемых операций перетаскивания.
122118
123119**DragFindWindow** выполняет поиск целевого перетаскиваемого окна (зависит от платформы).
124120
125121**DragInit** Инициализирует внутреннее управление, такое как захват мыши, и визуальную обратную связь.
126122
127123**DragInitControl** создает объект перетаскивания, затем вызывает `DragInit`.
128124
129125**SetCaptureControl** обрабатывает захват мышью.
130126
131127### События
132128
133129#### OnStartDrag/Dock
134130
135131Это событие происходит при запуске операции. Обработчик может предоставить настраиваемый `DockObject` для операции.
136132
137133#### OnDrag/DockOver
138134
139135Это событие уведомляет возможную цель, когда над ней перетаскивают объект. Обработчик может сигнализировать о принятии или отклонении отбрасывания.
140136
141137Событие сигнализирует не только о перемещении мыши, но и о том, когда мышь входит в целевой элемент управления или покидает его.
142138
143139Событие `OnDockOver` происходит на `DockSite`, а не в целевом элементе управления под указателем мыши. Обработчик может сигнализировать об отклонении сброса и может регулировать визуальную обратную связь. Событие происходит, когда мышь входит в элемент управления или выходит из него, а также при каждом движении мыши.
144140
145141#### OnDrag/DockDrop
146142
147143Это событие уведомляет целевой элемент управления об окончании операции перетаскивания.
148144
149145#### OnEndDrag/Dock
150146
151147Это событие уведомляет исходный элемент управления об окончании операции перетаскивания.
152148
153149### События, специфические для стыковки
154150
155151(этот раздел нужно переместить в раздел стыковки)
156152
157153#### OnGetSiteInfo
158154
159155Это событие происходит во время поиска подходящего стыковочного узла. Несколько стыковочных узлов могут перекрывать друг друга, располагаться в перекрывающихся окнах или могут быть невидимыми. Каждый кандидат должен предоставить прямоугольник влияния (область захвата) и может сигнализировать об отклонении сброса элемента управления. Диспетчер перетаскивания выбирает наиболее подходящий стыковочный узел в качестве цели для последующих событий `OnDockOver`.
160156
161157#### OnUnDock
162158
163159Это событие происходит, когда перетаскиваемый элемент управления необходимо удалить со стыковочного узла.
164160
165161Непонятно, что должно произойти, если обработчик откажется отстыковывать элемент управления.
166162
167163## LCL реализация
168164
169165Реализация механизма перетаскивания в Delphi имеет много недостатков, особенно в части стыковки. До сих пор никто, кажется, не понимал деталей модели перетаскивания Delphi, большая часть кода была смоделирована после VCL и, следовательно, реализует точно такое же неправильное поведение. Этот раздел и, что более важно, страница, посвященная стыковке, должны пролить свет на модель перетаскивания в целом, а также на серьезные недостатки дизайна и реализации в реализации Delphi.
170166
171167Текущая реализация LCL использует объект DragManager и исполнителей drag/dock вместо вспомогательных функций. Причина этого критического изменения неясна - кто сможет объяснить?
172168
173169Хотя модель Delphi должна быть предоставлена ​​по соображениям совместимости, введение класса и объекта DragManager(singleton) позволяет реализовать альтернативный диспетчер перетаскивания с улучшенным поведением и позволяет приложению выбирать наиболее подходящий диспетчер.
174170
175171### DragManager
176172
177173Этот объект обрабатывает захват ввода, предоставляет вспомогательные методы и сохраняет параметры перетаскивания и стыковки.
178174
179175Сомнительно, чтобы объекту перетаскивания не разрешалось обрабатывать ввод пользователя. Это делает систему более безопасной, но не позволяет приложениям расширять пользовательский интерфейс для определенных операций перетаскивания.
180176
181177Для вспомогательных методов требуются разные вызовы, но это безвредно, если они вызываются только из компонентов LCL.
182178
183179Более важным является хранение параметров перетаскивания в объекте DragManager, которые исчезают при установке другого диспетчера перетаскивания. Это фатально в случае списка зарегистрированных стыковочных узлов :-(
184180
185181### Исполнители механизма перетаскивания
186182
187183Внутреннее использование разных классов для перетаскивания и стыковки допустимо, поскольку процедуры значительно различаются между операциями drag-drop и drag-dock. Тем не менее общие действия должны использовать общий код, например унаследован от общего базового класса.
188184
189185## Диаграмма последовательности сообщений или диаграмма последовательности UML
190186
191187Предоставлено Tom Verhoeff
192188
193189Что-то вроде линий (это для Delphi; просмотр с моноширинным шрифтом):
194190
195191`                  Main            Drag          (Optional)        Drag`\
196192`  User            Event          Source           Drag           Target`\
197193` (Mouse)          Loop           Control          Object         Control`\
198194`    =               =               =                               =`\
199195`    |               |               |                               |`\
200196`   1|--MouseDown--->|               |                               |`\
201197`    |              2#--OnMouseDown->|                               |`\
202198`    |               |              3#                               |`\
203199`    |              4#<-BeginDrag()--#                               |`\
204200`    |               #               #                               |`\
205201`    |               |               |                               |`\
206202`    |              5#--OnStartDrag->|                               |`\
207203`    |               #              6#----Create---->=               |`\
208204`    |               |               #               |               |`\
209205`   7|--MouseMove--->|               |               |               |`\
210206`    |              8#-----------------------------------OnDragOver->|`\
211207`    |               #               |               |              9#`\
212208`    |               #               |               |               #`\
213209`    |               |               |               |               |`\
214210`  10|---MouseUp---->|               |               |               |`\
215211`    |             11#-----------------------------------OnDragDrop->|`\
216212`    |               #               |               |             12#`\
217213`    |               #               |               |               #`\
218214`    |               #---OnEndDrag-->|               |               |`\
219215`    |               |             13#----Destroy--->=               |`\
220216`    |               |               |                               |`
221217
222218`DragObject` не является обязательным. Может быть несколько потенциальных целей. Цикл главного события определяет, какое событие и какому объекту отправлять, в зависимости от действия пользователя, положения курсора и текущего состояния графического интерфейса.
223219
2242204 некоторым образом запускает 5 есть задержка в зависимости от Boolean-параметра Immediate в `BeginDrag`).
225221
2262226 определяет местоположение `MouseDown` и может использовать это для принятия решения, что перетаскивается (через `GetCursorPos`, `ScreenToClient`, `MouseToCell`). Он может вызывать `SetDragImage` для визуализации перетаскиваемого объекта.
227223
228224Цепочка 7, 8, 9 может повторяться; 9 указывает на приемлемость сброса компонента на эту цель через Boolean-параметр `var Accepted`.
229225
23022612 вызывается только в том случае, если непосредственно предшествующий событию `OnDragOver` этой цели вернул `Accepted = True`; т.е. `Accepted` - предварительное условие 12.
231227
23222813 получает параметр `Target`, чтобы узнать, где был принят брошенный компонент; Если `Target=nil`, то сброс компонента был отменен пользователем (`MouseUp` в недопустимом месте), а `OnDragDrop` не было выполнено; Если `Target<>nil`, то `OnDragDrop` уже был выполнен на `Target`. Таким образом, работа, связанная с фактическим сбросом, делится на `OnDragDrop` (для действий в целевом объекте) и `OnEndDrag` (для действий в источнике). Поскольку объект перетаскивания необходимо уничтожить, независимо от того, было ли перетаскивание успешным или отмененным, лучше всего вызывать `Free` (или `Destroy`) только в обработчике `OnEndDrag`.
233229
234230В любой момент может быть активным только одно перетаскивание; Он либо завершается сбросом компоента, либо отменой сброса.
235231
236232Есть много других вещей, которые нужно включить, но это основная часть. (Можно также посмотреть [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).)
237233
238234Надеюсь, это поможет. Tom
239235
240236## См. также
241237
242238- [[Drag and Drop sample/ru|Примеры Drag and Drop]]
243239
244240---
245241*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