Difference between revisions of "LCL Drag Drop/ru"

From Lazarus wiki
Jump to navigationJump to search
(Created page with "{{LCL Drag Drop}} <h2>Руководство DoDi по Dragging (перетаскиванию), Dropping (бросанию компонента) и Docking (стыковке)<...")
 
 
(18 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
{{LCL Drag Drop}}
 
{{LCL Drag Drop}}
<h2>Руководство DoDi по Dragging (перетаскиванию), Dropping (бросанию компонента) и Docking (стыковке)</h2>
 
  
Элементы управления в графическом интерфейсе можно захватить с помощью мыши, перетащить в другие места и перетащить на другие элементы управления или на рабочий стол. Когда перетаскиваемый элемент управления перемещается в другое место, операция называется [[LCL Drag Dock|"docking"]]. В противном случае исходный и целевой элементы управления могут обмениваться информацией или взаимодействовать любым другим способом, что называется операцией "drag-drop" (тащи-бросай).
 
  
But all that does not work by itself, an application must set up the allowed operations so that the LCL knows what to do with every control in a GUI. Sophisticated operations require further code for managing the visual feedback to the application user, and what will happen when the dragged control finally is dropped.
+
<h2> Руководство DoDi по Dragging (перетаскиванию), Dropping (бросанию компонента) и Docking (стыковке)</h2>
  
For simple docking support in entire applications you can use either [[EasyDockingManager]] or [[Anchor Docking]], that reduces the required adaptations to a minimum.
+
Элементы управления в графическом интерфейсе можно захватить с помощью мыши, перетащить в другие места и перетащить на другие элементы управления или на рабочий стол. Когда перетаскиваемый элемент управления перемещается в другое место, операция называется [[LCL Drag Dock/ru|"docking"]]. В противном случае исходный и целевой элементы управления могут обмениваться информацией или взаимодействовать любым другим способом, что называется операцией "drag-drop" (тащи-бросай).
 +
 
 +
Но все это не работает само по себе, приложение должно настроить разрешенные операции, чтобы LCL знал, что делать с каждым элементом управления в графическом интерфейсе. Сложные операции требуют дополнительного кода для управления визуальной обратной связью с пользователем приложения и того, что произойдет, когда перетаскиваемый элемент управления, наконец, будет отброшен.
 +
 
 +
Для простой поддержки стыковки во всех приложениях вы можете использовать [[EasyDockingManager]] или [[Anchor_Docking/ru|Anchor Docking]], что сводит к минимуму необходимые адаптации.
  
 
__TOC__
 
__TOC__
  
 
== Drag Drop ==
 
== Drag Drop ==
This is the simple and originally only dragging action. The players are:
+
Это простое и изначально действие только перетаскивания. Участники:
* A draggable source control in the GUI
+
* Позволяющий перетаскивать себя элемент управления в графическом интерфейсе
* A drag object, derived from TDragObject
+
* Объект перетаскивания, производный от <code>TDragObject</code>
* Helper functions
+
* Вспомогательные функции
* Events
+
* События
Now let's have a look at how these are working together.
+
Теперь давайте посмотрим, как они работают вместе.
 
 
=== The Drag Source ===
 
A control can be made draggable by setting its '''DragMode''' property to '''dmAutomatic'''. When the user presses the left mouse button over such a control, the mouse pointer changes into an dragging indicator until the user releases the button, or cancels the operation by pressing the Escape key. The indicator now follows the mouse movements and changes its shape, according to what will happen when the control is dropped in the current location.
 
  
Dragging also can be started in code, by calling ''SourceControl''.'''BeginDrag'''. In either case the helper procedure ''DragManager.DragStart'' is called to start dragging. The '''DragManager''' is a global instance implementing the actual dragging. It is initialized with the default ''TDragManagerDefault'' and can be replaced by the programmer (You). Depending on the control's '''DragKind''' a drag or a dock operation is started. The source control triggers an '''OnStartDrag''' event, where the programmer can provide a custom drag object. If no such object is provided, a default object (''TDragControlObject'' or ''TDragDockObject'') is created. Internal data structures for visual feedback and more are initialized.
+
=== Источник перетаскивания ===
 +
Элемент управления можно сделать перетаскиваемым, установив для его свойства <code>DragMode</code> значение <code>dmAutomatic</code>. Когда пользователь нажимает левую кнопку мыши на таком элементе управления, указатель мыши превращается в индикатор перетаскивания, пока пользователь не отпустит кнопку или не отменит операцию, нажав клавишу <code>Escape</code>. Индикатор теперь следует за движениями мыши и меняет свою форму в соответствии с тем, что произойдет, когда элемент управления будет брошен в текущее место.
  
===The Drag Object (TDragObject defined in unit controls)===
+
Перетаскивание также можно запустить в коде, вызвав <code>SourceControl.BeginDrag</code>. В любом случае вызывается вспомогательная процедура <code>DragManager.DragStart</code> для начала перетаскивания. <code>DragManager</code> - это глобальный экземпляр, реализующий фактическое перетаскивание. Он инициализируется значением по умолчанию <code>TDragManagerDefault</code> и может быть заменен программистом (Вами). В зависимости от <code>DragKind</code> элемента управления запускается операция перетаскивания или стыковки. Исходный элемент управления запускает событие <code>OnStartDrag</code>, в котором программист может предоставить пользовательский объект перетаскивания. Если такой объект не указан, создается объект по умолчанию (<code>TDragControlObject</code> или <code>TDragDockObject</code>). Инициализируются внутренние структуры данных для визуальной обратной связи и прочего.
  
A drag object receives all user input while dragging. In the default implementation it reacts by calling either DragMove or finally DragStop.
+
=== Перетаскиваемый объект (TDragObject, определенный в элементах управления модуля)===
 +
Перетаскиваемый объект получает весь пользовательский ввод при перетаскивании. В реализации по умолчанию он реагирует вызовом либо <code>DragMove</code>, либо <code>DragStop</code>.
  
The drag object also is responsible for the visual feedback:
+
Перетаскиваемый объект также отвечает за визуальную обратную связь:
  
*'''GetDragCursor''' returns the mouse pointer shape.
+
* <code>GetDragCursor</code> возвращает форму указателя мыши.
*'''GetDragImages''' returns a list of drag images, attachable to the mouse pointer.
+
* <code>GetDragImages</code> возвращает список перетаскиваемых изображений, прикрепляемых к указателю мыши.
*'''ShowDragImage''' and '''HideDragImage''' implement the visual feedback.  
+
* <code>ShowDragImage</code> и <code>HideDragImage</code> реализуют визуальную обратную связь.
  
The '''EndDrag''' method notifies the source control when dragging stops. In case of a cancel'd operation it invokes ''SourceControl''.'''DragCanceled''', followed by ''SourceControl''.'''DoEndDrag''' in either case, which raises an '''OnEndDrag''' event.
+
Метод <code>EndDrag</code> уведомляет исходный элемент управления об остановке перетаскивания. В случае отмены операции он вызывает <code>SourceControl.DragCanceled</code>, за которым следует <code>SourceControl.DoEndDrag</code> в любом случае, что вызывает событие <code>OnEndDrag</code>.
  
=== Events ===
+
=== События ===
  
 
==== OnStartDrag/Dock ====
 
==== OnStartDrag/Dock ====
This event occurs when the operation starts. The handler can provide a custom TDragObject or TDragDockObject for the operation.
+
Это событие происходит при запуске операции. Обработчик может предоставить настраиваемый <code>TDragObject</code> или <code>TDragDockObject</code> для операции.
  
 
==== OnDrag/DockOver ====
 
==== OnDrag/DockOver ====
This event notifies a possible target, when an object is dragged over it. The handler can signal to accept or reject a drop.
+
Это событие уведомляет возможную цель, когда над ней перетаскивают объект. Обработчик может сигнализировать о принятии или отклонении сброса компонента.
  
The event signals not only mouse moves, but also when the mouse enters or leaves a target control.
+
Событие сигнализирует не только о перемещении мыши, но и о том, когда мышь входит в зону целевого элемента управления или покидает ее.
  
An OnDockOver event occurs on the DockSite, not on the target control under the mouse. The handler can signal to deny an drop, and can adjust the visual feedback. The event occurs when the mouse enters or leaves a control, and with every single movement of the mouse.
+
Событие <code>OnDockOver</code> происходит на <code>DockSite</code>, а не в целевом элементе управления под указателем мыши. Обработчик может сигнализировать об отклонении сброса и может регулировать визуальную обратную связь. Событие происходит, когда мышь входит в зону элемента управления или выходит из нее, а также при каждом движении мыши.
  
 
==== OnDrag/DockDrop ====
 
==== OnDrag/DockDrop ====
This event notifies the target control of the end of a drag operation.
+
Это событие уведомляет целевой элемент управления об окончании операции перетаскивания.
  
 
==== OnEndDrag/Dock ====
 
==== OnEndDrag/Dock ====
This event notifies the source control of the end of a drag operation.
+
Это событие уведомляет исходный элемент управления об окончании операции перетаскивания.
  
== Major Shortcomings ==
+
== Основные недостатки ==
* The same flaws as Delphi's VCL.
+
* Те же недостатки, что и у Delphi VCL.
* No LCL framework (cross-platform) support for dragging from an LCL application to any other application. For this to work, LCL needs support for the XDND protocol under X11, OLE Drag-n-Drop support under Windows and Mac Drag Manager under OSX.
+
* Нет поддержки платформы LCL (кроссплатформа) для перетаскивания компонентов из приложения LCL в любое другое приложение. Чтобы это работало, LCL необходима поддержка протокола XDND в X11, поддержка OLE Drag-n-Drop в Windows и Mac Drag Manager в OSX.
* No LCL framework (cross-platform) support for receiving drops inside an LCL application from another application (desktop, file manager, etc). The OnDragDrop event handler always expects the Source of the drop to come through as a TObject descendant which will '''not''' be the case when dragging an object from another application to a LCL application.
+
* Нет поддержки платформы LCL (кроссплатформа) для приема сбрасываемых компонентов внутри приложения LCL из другого приложения (рабочего стола, файлового менеджера и т.д.). Обработчик событий <code>OnDragDrop</code> всегда ожидает, что перетаскиваемый компонент(Source) будет передаваться как потомок <code>TObject</code>, что '''не''' будет иметь место при перетаскивании объекта из другого приложения в приложение LCL.
  
 
=Delphi=
 
=Delphi=
  
 
== Drag Drop ==
 
== Drag Drop ==
This is the simple and originally only dragging action. The players are:
+
Это простое и изначально действие только перетаскивания. Участники:
* A draggable source control in the GUI
+
* Позволяющий перетаскивать себя элемент управления в графическом интерфейсе
* A drag object, derived from TDragObject
+
* Объект перетаскивания, производный от <code>TDragObject</code>
* Helper functions
+
* Вспомогательные функции
* Events
+
* События
Now let's have a look at how these are working together, based on the original Delphi implementation for clarity. The LCL implementation is somewhat different in the use of helper objects, details will be discussed later.
+
Теперь давайте посмотрим, как они работают вместе, на основе исходной реализации Delphi для ясности. Реализация LCL несколько отличается в использовании вспомогательных объектов, подробности будут обсуждены позже.
  
=== The Drag Source ===
+
=== Перетаскиваемый источник(объект) ===
A control can be made draggable by setting its '''DragMode''' property to '''dmAutomatic'''. When the user presses the left mouse button over such a control, the mouse pointer changes into an dragging indicator until the user releases the button, or cancels the operation by pressing the Escape key. The indicator now follows the mouse movements and changes its shape, according to what will happen when the control is dropped in the current location.
+
Элемент управления можно сделать перетаскиваемым, установив для его свойства <code>DragMode</code> значение <code>dmAutomatic</code>. Когда пользователь нажимает левую кнопку мыши на таком элементе управления, указатель мыши превращается в индикатор перетаскивания, пока пользователь не отпустит кнопку или не отменит операцию, нажав клавишу Escape. Индикатор теперь следует за движениями мыши и меняет свою форму в соответствии с тем, что произойдет, когда элемент управления будет брошен в текущее место.
  
Dragging also can be started in code, by calling source.BeginDrag. In either case the helper procedure DragInitControl is called to start dragging. The source control raises an StartDrag event, where the application can provide a custom drag object. If no such object is provided, a default object is created. Internal data structures for visual feedback and more are initialized. Then DragTo is called to show that dragging has started.
+
Перетаскивание также можно запустить программно, вызвав <code>Source.BeginDrag</code>. В любом случае вызывается вспомогательная процедура <code>DragInitControl</code> для начала перетаскивания. Исходный элемент управления вызывает событие <code>StartDrag</code>, в котором приложение может предоставить настраиваемый объект перетаскивания. Если такой объект не указан, создается объект по умолчанию. Инициализируются внутренние структуры данных для визуальной обратной связи и прочего. Затем вызывается <code>DragTo</code>, чтобы показать, что перетаскивание началось.
  
=== The Drag Object (TDragObject defined in unit controls) ===
+
=== Перетаскиваемый объект (TDragObject, определенный в элементах управления модуля) ===
A drag object receives all user input while dragging. In the default implementation it reacts by calling either DragTo or finally DragDone.
+
Перетаскиваемый объект получает весь пользовательский ввод при перетаскивании. В реализации по умолчанию он реагирует вызовом либо <code>DragMove</code>, либо <code>DragStop</code>.
  
The drag object also is responsible for the visual feedback:
+
Перетаскиваемый объект также отвечает за визуальную обратную связь:
* GetDragCursor returns the mouse pointer shape.
 
* GetDragImages returns a list of drag images, to be attached to the mouse pointer.
 
* ShowDragImage and HideDragImage implement the visual feedback.
 
  
The Finished method notifies the source control when dragging stops. In case of a cancel'd operation it invokes source.DragCanceled, followed by source.DoEndDrag in either case, which raises an EndDrag event.
+
* <code>GetDragCursor</code> возвращает форму указателя мыши.
 +
* <code>GetDragImages</code> возвращает список перетаскиваемых изображений, прикрепляемых к указателю мыши.
 +
* <code>ShowDragImage</code> и <code>HideDragImage</code> реализуют визуальную обратную связь.
  
==== LCL Modifications ====
+
Метод <code>EndDrag</code> уведомляет исходный элемент управления об остановке перетаскивания. В случае отмены операции он вызывает <code>SourceControl.DragCanceled</code>, за которым следует <code>SourceControl.DoEndDrag</code> в любом случае, что вызывает событие <code>OnEndDrag</code>.
The current LCL implementation has removed input processing from the drag object, so that the application can no more customize the UI. And the LCL implementation is far from being okay :-(
 
  
Also the drag object is bypassed in the presentation of the visual feedback.
 
  
=== Helper Functions ===
+
==== Модификации LCL ====
Helper functions primarily allow for access to protected properties and methods of the controls and other classes, from outside the Controls unit. They also can perform common tasks, and can encapsulate platform specific and other implementation details.
+
Текущая реализация LCL удалила обработку ввода из объекта перетаскивания, так что приложение больше не может настраивать пользовательский интерфейс. И реализация LCL далеко не в порядке :-(
  
==== General Helpers ====
+
Также объект перетаскивания игнорируется при представлении визуальной обратной связи.
'''DragTo''' (Delphi only, not present in LCL) determines the current drag target, sends notifications to the involved controls, and manages the visual feedback. It also handles the delayed start of a dragging operation, i.e. can wait for the mouse moving away far enough from the starting point.
 
  
'''DragDone''' (Delphi only, not present in LCL) terminates dragging, either with a drop or cancel of the operation.
+
=== Вспомогательные (Helper) функции ===
 +
Вспомогательные функции в первую очередь позволяют получить доступ к защищенным свойствам и методам элементов управления и других классов извне модуля <code>Controls</code>. Они также могут выполнять общие задачи и могут инкапсулировать специфические для платформы и другие детали реализации.
  
'''CancelDrag''' (Delphi only, do not confuse with the LCL global procedure CancelDrag) is protected against false calls, otherwise it defers further actions to DragDone.
+
==== Общие помощники ====
 +
'''DragTo''' (Только для Delphi, отсутствует в LCL) определяет текущую цель перетаскивания, отправляет уведомления задействованным элементам управления и управляет визуальной обратной связью. Он также обрабатывает отложенный старт операции перетаскивания, то есть может ждать, пока мышь не уйдет достаточно далеко от начальной точки.
  
==== Special Helpers ====
+
'''DragDone''' (Только для Delphi, отсутствует в LCL) завершает перетаскивание либо cбросом элемента управления в контейнер, либо отменой операции.
Some more public helper procedures exist, which are undocumented in Delphi and are not required for customized dragging operations.
 
  
'''DragFindWindow''' searches for a drag target window (platform specific).
+
'''CancelDrag''' (Только для Delphi, не путайте с глобальной процедурой LCL <code>CancelDrag</code>) защищен от ложных вызовов, иначе дальнейшие действия откладываются на <code>DragDone</code>.
  
'''DragInit''' initializes the internal management, like mouse capture, and the visual feedback.
+
==== Специальные помощники ====
 +
Существует еще несколько общедоступных вспомогательных процедур, которые недокументированы в Delphi и не требуются для настраиваемых операций перетаскивания.
  
'''DragInitControl''' creates the drag object, then calls DragInit.
+
'''DragFindWindow''' выполняет поиск целевого перетаскиваемого окна (зависит от платформы).
  
'''SetCaptureControl''' handles the mouse capture.
+
'''DragInit''' Инициализирует внутреннее управление, такое как захват мыши, и визуальную обратную связь.
  
=== Events ===
+
'''DragInitControl''' создает объект перетаскивания, затем вызывает <code>DragInit</code>.
 +
 
 +
'''SetCaptureControl''' обрабатывает захват мышью.
 +
 
 +
=== События ===
  
 
==== OnStartDrag/Dock ====
 
==== OnStartDrag/Dock ====
This event occurs when the operation starts. The handler can provide a custom DockObject for the operation.
+
Это событие происходит при запуске операции. Обработчик может предоставить настраиваемый <code>DockObject</code> для операции.
  
 
==== OnDrag/DockOver ====
 
==== OnDrag/DockOver ====
This event notifies a possible target, when an object is dragged over it. The handler can signal to accept or reject an drop.
+
Это событие уведомляет возможную цель, когда над ней перетаскивают объект. Обработчик может сигнализировать о принятии или отклонении отбрасывания.
  
The event signals not only mouse moves, but also when the mouse enters or leaves a target control.
+
Событие сигнализирует не только о перемещении мыши, но и о том, когда мышь входит в целевой элемент управления или покидает его.
  
An OnDockOver event occurs on the DockSite, not on the target control under the mouse. The handler can signal to deny an drop, and can adjust the visual feedback. The event occurs when the mouse enters or leaves a control, and with every single movement of the mouse.
+
Событие <code>OnDockOver</code> происходит на <code>DockSite</code>, а не в целевом элементе управления под указателем мыши. Обработчик может сигнализировать об отклонении сброса и может регулировать визуальную обратную связь. Событие происходит, когда мышь входит в элемент управления или выходит из него, а также при каждом движении мыши.
  
 
==== OnDrag/DockDrop ====
 
==== OnDrag/DockDrop ====
This event notifies the target control of the end of a drag operation.
+
Это событие уведомляет целевой элемент управления об окончании операции перетаскивания.
  
 
==== OnEndDrag/Dock ====
 
==== OnEndDrag/Dock ====
This event notifies the source control of the end of a drag operation.
+
Это событие уведомляет исходный элемент управления об окончании операции перетаскивания.
 +
 
 +
=== События, специфические для стыковки ===
 +
(этот раздел нужно переместить в раздел стыковки)
  
=== Docking specific Events ===
 
(this section should be moved to docking topic)
 
 
==== OnGetSiteInfo ====
 
==== OnGetSiteInfo ====
This event occurs during the search for a qualifying dock site. Multiple dock sites can overlap each other, reside in overlapping windows, or can be invisible. Every candidate must supply an influence rectangle (catching area), and can signal to reject an drop. The drag manager selects the most appropriate dock site as the target for subsequent OnDockOver events.
+
Это событие происходит во время поиска подходящего стыковочного узла. Несколько стыковочных узлов могут перекрывать друг друга, располагаться в перекрывающихся окнах или могут быть невидимыми. Каждый кандидат должен предоставить прямоугольник влияния (область захвата) и может сигнализировать об отклонении сброса элемента управления. Диспетчер перетаскивания выбирает наиболее подходящий стыковочный узел в качестве цели для последующих событий <code>OnDockOver</code>.
  
 
==== OnUnDock ====
 
==== OnUnDock ====
This event occurs when the dragged control has to be removed from its host dock site.
+
Это событие происходит, когда перетаскиваемый элемент управления необходимо удалить со стыковочного узла.
  
It's unclear what should happen when the handler denies to undock the control.
+
Непонятно, что должно произойти, если обработчик откажется отстыковывать элемент управления.
  
== LCL Implementation ==
+
== LCL реализация ==
The Delphi implementation of dragging has many flaws, in detail with regards to docking. Until now nobody seems to understand the details of the Delphi dragging model, most code was modeled after the VCL and consequently implements exactly the same misbehaviour. This section, and more important the docking specific page, shall shed some light on the dragging model in general and the severe design and implementation flaws in the Delphi implementation.
+
Реализация механизма перетаскивания в Delphi имеет много недостатков, особенно в части стыковки. До сих пор никто, кажется, не понимал деталей модели перетаскивания Delphi, большая часть кода была смоделирована после VCL и, следовательно, реализует точно такое же неправильное поведение. Этот раздел и, что более важно, страница, посвященная стыковке, должны пролить свет на модель перетаскивания в целом, а также на серьезные недостатки дизайна и реализации в реализации Delphi.
  
The current LCL implementation uses a DragManager object and drag/dock performers instead of helper functions. The reason for this breaking change is unclear - who can explain?
+
Текущая реализация LCL использует объект DragManager и исполнителей drag/dock вместо вспомогательных функций. Причина этого критического изменения неясна - кто сможет объяснить?
  
While the Delphi model must be provided for compatibility reasons, the introduction of a DragManager class and object (singleton) allows for the implementation of an alternative drag manager, with improved behaviour, and allows an application to select the most appropriate manager.
+
Хотя модель Delphi должна быть предоставлена ​​по соображениям совместимости, введение класса и объекта DragManager(singleton) позволяет реализовать альтернативный диспетчер перетаскивания с улучшенным поведением и позволяет приложению выбирать наиболее подходящий диспетчер.
  
 
=== DragManager ===
 
=== DragManager ===
This object handles the captured input, provides helper methods, and stores dragging and docking parameters.
+
Этот объект обрабатывает захват ввода, предоставляет вспомогательные методы и сохраняет параметры перетаскивания и стыковки.
  
It's questionable whether the drag object shall not be allowed to handle user input. It makes the system safer, but disallows applications to extend the user interface for specific dragging operations.
+
Сомнительно, чтобы объекту перетаскивания не разрешалось обрабатывать ввод пользователя. Это делает систему более безопасной, но не позволяет приложениям расширять пользовательский интерфейс для определенных операций перетаскивания.
  
The helper methods require different calls, but this is harmless as long as they are only called from LCL components.
+
Для вспомогательных методов требуются разные вызовы, но это безвредно, если они вызываются только из компонентов LCL.
  
More critical is the storage of dragging parameters in the DragManager object, which vanish when a different drag manager is installed. This is fatal in case of the list of registered dock sites :-(
+
Более важным является хранение параметров перетаскивания в объекте DragManager, которые исчезают при установке другого диспетчера перетаскивания. Это фатально в случае списка зарегистрированных стыковочных узлов :-(
  
=== Drag Performers ===
+
=== Исполнители механизма перетаскивания  ===
The internal use of different classes for dragging and docking is okay in so far, as the procedures differ significantly between drag-drop and drag-dock operations. Nonetheless common actions should use common code, e.g. inherited from the common base class.
+
Внутреннее использование разных классов для перетаскивания и стыковки допустимо, поскольку процедуры значительно различаются между операциями drag-drop и drag-dock. Тем не менее общие действия должны использовать общий код, например унаследован от общего базового класса.
  
== Message Sequence Chart or UML Sequence Diagram ==
+
== Диаграмма последовательности сообщений или диаграмма последовательности UML ==
  
Provided by Tom Verhoeff
+
Предоставлено Tom Verhoeff
  
Something along the lines (this is for Delphi; view with monospaced font):
+
Что-то вроде линий (это для Delphi; просмотр с моноширинным шрифтом):
  
 
                   Main            Drag          (Optional)        Drag
 
                   Main            Drag          (Optional)        Drag
Line 190: Line 194:
  
  
The DragObject is optional. There could be multiple candidate targets.
+
<code>DragObject</code> не является обязательным. Может быть несколько потенциальных целей.
The Main Event Loop determines which event to send to which object,
+
Цикл главного события определяет, какое событие и какому объекту отправлять,
depending on the user action, the cursor location, and the current state
+
в зависимости от действия пользователя, положения курсора и текущего состояния
of the GUI.
+
графического интерфейса.
 
 
4 somehow triggers 5 (there is a delay depending on the Boolean parameter
 
Immediate to BeginDrag).
 
  
6 determines the location of the MouseDown and may use this to decide on
+
4 некоторым образом запускает 5 есть задержка в зависимости от Boolean-параметра
what gets dragged (through GetCursorPos, ScreenToClient, MouseToCell).
+
Immediate в <code>BeginDrag</code>).
It can call SetDragImage to visualize a thing being dragged.
 
  
The chain 7, 8, 9 can occur repeatedly; 9 indicates acceptability
+
6 определяет местоположение <code>MouseDown</code> и может использовать это для принятия решения,
of a drop at this target via the Boolean var parameter Accepted.
+
что перетаскивается (через <code>GetCursorPos</code>, <code>ScreenToClient</code>, <code>MouseToCell</code>).
 +
Он может вызывать <code>SetDragImage</code> для визуализации перетаскиваемого объекта.
  
12 is called only if the immediately preceding OnDragOver of this target
+
Цепочка 7, 8, 9 может повторяться; 9 указывает на приемлемость
returned Accepted = True; i.e. Accepted is a precondition of 12.
+
сброса компонента на эту цель через Boolean-параметр <code>var Accepted</code>.
  
13 receives the parameter Target to know where the drop was accepted;
+
12 вызывается только в том случае, если непосредственно предшествующий событию <code>OnDragOver</code> этой цели
if Target = nil, then the drop was cancelled by the user
+
вернул <code>Accepted = True</code>; т.е. <code>Accepted</code> - предварительное условие 12.
(MouseUp at an unacceptable location), and OnDragDrop was not performed;
 
if Target <> nil, then OnDragDrop was already performed at the Target.
 
The work associated with an actual drop is thus divided over the
 
OnDragDrop (for actions at the target) and OnEndDrag (for actions at
 
the source). Since a Drag Object needs to be destroyed, independent
 
of whether the drop was successful or canceled, it is best to call
 
Free (or Destroy) only in the OnEndDrag handler.
 
  
Only one drag can be active at any moment; it either ends in a drop or is
+
13 получает параметр <code>Target</code>, чтобы узнать, где был принят брошенный компонент;  
canceled.
+
Если <code>Target=nil</code>, то сброс компонента был отменен пользователем
 +
(<code>MouseUp</code> в недопустимом месте), а <code>OnDragDrop</code> не было выполнено;
 +
Если <code>Target<>nil</code>, то <code>OnDragDrop</code> уже был выполнен на <code>Target</code>.
 +
Таким образом, работа, связанная с фактическим сбросом, делится
 +
на <code>OnDragDrop</code> (для действий в целевом объекте) и
 +
<code>OnEndDrag</code> (для действий в источнике). Поскольку объект перетаскивания необходимо
 +
уничтожить, независимо от того, было ли перетаскивание успешным или отмененным,
 +
лучше всего вызывать <code>Free</code> (или <code>Destroy</code>) только в обработчике <code>OnEndDrag</code>.
  
There are various other things to be included, but this is the fundamental
+
В любой момент может быть активным только одно перетаскивание; Он либо завершается
part.  (Maybe also see [http://www.delphi-central.com/drag.aspx Delphi-Central] or
+
сбросом компоента, либо отменой сброса.
[http://www.podgoretsky.com/ftp/Docs/Delphi/D5/dg/controls.html "Implementing drag-and-drop in controls"].)
 
  
I hope this is helpful. Tom
+
Есть много других вещей, которые нужно включить, но это основная часть. (Можно также
 +
посмотреть [http://www.delphi-central.com/drag.aspx Delphi-Central] или
 +
[https://web.archive.org/web/20120422052012/http://www.podgoretsky.com/ftp/docs/Delphi/D5/dg/controls.html "Implementing drag-and-drop in controls"].)
  
== See also ==
+
Надеюсь, это поможет. Tom
* [[Drag and Drop sample]]
 
  
[[Category:LCL]]
+
== См. также ==
[[Category:GUI]]
+
* [[Drag_and_Drop_sample/ru| Примеры Drag and Drop]]
[[Category:DragAndDrop]]
 

Latest revision as of 21:12, 19 August 2021

English (en) français (fr) русский (ru)


Руководство DoDi по Dragging (перетаскиванию), Dropping (бросанию компонента) и Docking (стыковке)

Элементы управления в графическом интерфейсе можно захватить с помощью мыши, перетащить в другие места и перетащить на другие элементы управления или на рабочий стол. Когда перетаскиваемый элемент управления перемещается в другое место, операция называется "docking". В противном случае исходный и целевой элементы управления могут обмениваться информацией или взаимодействовать любым другим способом, что называется операцией "drag-drop" (тащи-бросай).

Но все это не работает само по себе, приложение должно настроить разрешенные операции, чтобы LCL знал, что делать с каждым элементом управления в графическом интерфейсе. Сложные операции требуют дополнительного кода для управления визуальной обратной связью с пользователем приложения и того, что произойдет, когда перетаскиваемый элемент управления, наконец, будет отброшен.

Для простой поддержки стыковки во всех приложениях вы можете использовать EasyDockingManager или Anchor Docking, что сводит к минимуму необходимые адаптации.

Drag Drop

Это простое и изначально действие только перетаскивания. Участники:

  • Позволяющий перетаскивать себя элемент управления в графическом интерфейсе
  • Объект перетаскивания, производный от TDragObject
  • Вспомогательные функции
  • События

Теперь давайте посмотрим, как они работают вместе.

Источник перетаскивания

Элемент управления можно сделать перетаскиваемым, установив для его свойства DragMode значение dmAutomatic. Когда пользователь нажимает левую кнопку мыши на таком элементе управления, указатель мыши превращается в индикатор перетаскивания, пока пользователь не отпустит кнопку или не отменит операцию, нажав клавишу Escape. Индикатор теперь следует за движениями мыши и меняет свою форму в соответствии с тем, что произойдет, когда элемент управления будет брошен в текущее место.

Перетаскивание также можно запустить в коде, вызвав SourceControl.BeginDrag. В любом случае вызывается вспомогательная процедура DragManager.DragStart для начала перетаскивания. DragManager - это глобальный экземпляр, реализующий фактическое перетаскивание. Он инициализируется значением по умолчанию TDragManagerDefault и может быть заменен программистом (Вами). В зависимости от DragKind элемента управления запускается операция перетаскивания или стыковки. Исходный элемент управления запускает событие OnStartDrag, в котором программист может предоставить пользовательский объект перетаскивания. Если такой объект не указан, создается объект по умолчанию (TDragControlObject или TDragDockObject). Инициализируются внутренние структуры данных для визуальной обратной связи и прочего.

Перетаскиваемый объект (TDragObject, определенный в элементах управления модуля)

Перетаскиваемый объект получает весь пользовательский ввод при перетаскивании. В реализации по умолчанию он реагирует вызовом либо DragMove, либо DragStop.

Перетаскиваемый объект также отвечает за визуальную обратную связь:

  • GetDragCursor возвращает форму указателя мыши.
  • GetDragImages возвращает список перетаскиваемых изображений, прикрепляемых к указателю мыши.
  • ShowDragImage и HideDragImage реализуют визуальную обратную связь.

Метод EndDrag уведомляет исходный элемент управления об остановке перетаскивания. В случае отмены операции он вызывает SourceControl.DragCanceled, за которым следует SourceControl.DoEndDrag в любом случае, что вызывает событие OnEndDrag.

События

OnStartDrag/Dock

Это событие происходит при запуске операции. Обработчик может предоставить настраиваемый TDragObject или TDragDockObject для операции.

OnDrag/DockOver

Это событие уведомляет возможную цель, когда над ней перетаскивают объект. Обработчик может сигнализировать о принятии или отклонении сброса компонента.

Событие сигнализирует не только о перемещении мыши, но и о том, когда мышь входит в зону целевого элемента управления или покидает ее.

Событие OnDockOver происходит на DockSite, а не в целевом элементе управления под указателем мыши. Обработчик может сигнализировать об отклонении сброса и может регулировать визуальную обратную связь. Событие происходит, когда мышь входит в зону элемента управления или выходит из нее, а также при каждом движении мыши.

OnDrag/DockDrop

Это событие уведомляет целевой элемент управления об окончании операции перетаскивания.

OnEndDrag/Dock

Это событие уведомляет исходный элемент управления об окончании операции перетаскивания.

Основные недостатки

  • Те же недостатки, что и у Delphi VCL.
  • Нет поддержки платформы LCL (кроссплатформа) для перетаскивания компонентов из приложения LCL в любое другое приложение. Чтобы это работало, LCL необходима поддержка протокола XDND в X11, поддержка OLE Drag-n-Drop в Windows и Mac Drag Manager в OSX.
  • Нет поддержки платформы LCL (кроссплатформа) для приема сбрасываемых компонентов внутри приложения LCL из другого приложения (рабочего стола, файлового менеджера и т.д.). Обработчик событий OnDragDrop всегда ожидает, что перетаскиваемый компонент(Source) будет передаваться как потомок TObject, что не будет иметь место при перетаскивании объекта из другого приложения в приложение LCL.

Delphi

Drag Drop

Это простое и изначально действие только перетаскивания. Участники:

  • Позволяющий перетаскивать себя элемент управления в графическом интерфейсе
  • Объект перетаскивания, производный от TDragObject
  • Вспомогательные функции
  • События

Теперь давайте посмотрим, как они работают вместе, на основе исходной реализации Delphi для ясности. Реализация LCL несколько отличается в использовании вспомогательных объектов, подробности будут обсуждены позже.

Перетаскиваемый источник(объект)

Элемент управления можно сделать перетаскиваемым, установив для его свойства DragMode значение dmAutomatic. Когда пользователь нажимает левую кнопку мыши на таком элементе управления, указатель мыши превращается в индикатор перетаскивания, пока пользователь не отпустит кнопку или не отменит операцию, нажав клавишу Escape. Индикатор теперь следует за движениями мыши и меняет свою форму в соответствии с тем, что произойдет, когда элемент управления будет брошен в текущее место.

Перетаскивание также можно запустить программно, вызвав Source.BeginDrag. В любом случае вызывается вспомогательная процедура DragInitControl для начала перетаскивания. Исходный элемент управления вызывает событие StartDrag, в котором приложение может предоставить настраиваемый объект перетаскивания. Если такой объект не указан, создается объект по умолчанию. Инициализируются внутренние структуры данных для визуальной обратной связи и прочего. Затем вызывается DragTo, чтобы показать, что перетаскивание началось.

Перетаскиваемый объект (TDragObject, определенный в элементах управления модуля)

Перетаскиваемый объект получает весь пользовательский ввод при перетаскивании. В реализации по умолчанию он реагирует вызовом либо DragMove, либо DragStop.

Перетаскиваемый объект также отвечает за визуальную обратную связь:

  • GetDragCursor возвращает форму указателя мыши.
  • GetDragImages возвращает список перетаскиваемых изображений, прикрепляемых к указателю мыши.
  • ShowDragImage и HideDragImage реализуют визуальную обратную связь.

Метод EndDrag уведомляет исходный элемент управления об остановке перетаскивания. В случае отмены операции он вызывает SourceControl.DragCanceled, за которым следует SourceControl.DoEndDrag в любом случае, что вызывает событие OnEndDrag.


Модификации LCL

Текущая реализация LCL удалила обработку ввода из объекта перетаскивания, так что приложение больше не может настраивать пользовательский интерфейс. И реализация LCL далеко не в порядке :-(

Также объект перетаскивания игнорируется при представлении визуальной обратной связи.

Вспомогательные (Helper) функции

Вспомогательные функции в первую очередь позволяют получить доступ к защищенным свойствам и методам элементов управления и других классов извне модуля Controls. Они также могут выполнять общие задачи и могут инкапсулировать специфические для платформы и другие детали реализации.

Общие помощники

DragTo (Только для Delphi, отсутствует в LCL) определяет текущую цель перетаскивания, отправляет уведомления задействованным элементам управления и управляет визуальной обратной связью. Он также обрабатывает отложенный старт операции перетаскивания, то есть может ждать, пока мышь не уйдет достаточно далеко от начальной точки.

DragDone (Только для Delphi, отсутствует в LCL) завершает перетаскивание либо cбросом элемента управления в контейнер, либо отменой операции.

CancelDrag (Только для Delphi, не путайте с глобальной процедурой LCL CancelDrag) защищен от ложных вызовов, иначе дальнейшие действия откладываются на DragDone.

Специальные помощники

Существует еще несколько общедоступных вспомогательных процедур, которые недокументированы в Delphi и не требуются для настраиваемых операций перетаскивания.

DragFindWindow выполняет поиск целевого перетаскиваемого окна (зависит от платформы).

DragInit Инициализирует внутреннее управление, такое как захват мыши, и визуальную обратную связь.

DragInitControl создает объект перетаскивания, затем вызывает DragInit.

SetCaptureControl обрабатывает захват мышью.

События

OnStartDrag/Dock

Это событие происходит при запуске операции. Обработчик может предоставить настраиваемый DockObject для операции.

OnDrag/DockOver

Это событие уведомляет возможную цель, когда над ней перетаскивают объект. Обработчик может сигнализировать о принятии или отклонении отбрасывания.

Событие сигнализирует не только о перемещении мыши, но и о том, когда мышь входит в целевой элемент управления или покидает его.

Событие OnDockOver происходит на DockSite, а не в целевом элементе управления под указателем мыши. Обработчик может сигнализировать об отклонении сброса и может регулировать визуальную обратную связь. Событие происходит, когда мышь входит в элемент управления или выходит из него, а также при каждом движении мыши.

OnDrag/DockDrop

Это событие уведомляет целевой элемент управления об окончании операции перетаскивания.

OnEndDrag/Dock

Это событие уведомляет исходный элемент управления об окончании операции перетаскивания.

События, специфические для стыковки

(этот раздел нужно переместить в раздел стыковки)

OnGetSiteInfo

Это событие происходит во время поиска подходящего стыковочного узла. Несколько стыковочных узлов могут перекрывать друг друга, располагаться в перекрывающихся окнах или могут быть невидимыми. Каждый кандидат должен предоставить прямоугольник влияния (область захвата) и может сигнализировать об отклонении сброса элемента управления. Диспетчер перетаскивания выбирает наиболее подходящий стыковочный узел в качестве цели для последующих событий OnDockOver.

OnUnDock

Это событие происходит, когда перетаскиваемый элемент управления необходимо удалить со стыковочного узла.

Непонятно, что должно произойти, если обработчик откажется отстыковывать элемент управления.

LCL реализация

Реализация механизма перетаскивания в Delphi имеет много недостатков, особенно в части стыковки. До сих пор никто, кажется, не понимал деталей модели перетаскивания Delphi, большая часть кода была смоделирована после VCL и, следовательно, реализует точно такое же неправильное поведение. Этот раздел и, что более важно, страница, посвященная стыковке, должны пролить свет на модель перетаскивания в целом, а также на серьезные недостатки дизайна и реализации в реализации Delphi.

Текущая реализация LCL использует объект DragManager и исполнителей drag/dock вместо вспомогательных функций. Причина этого критического изменения неясна - кто сможет объяснить?

Хотя модель Delphi должна быть предоставлена ​​по соображениям совместимости, введение класса и объекта DragManager(singleton) позволяет реализовать альтернативный диспетчер перетаскивания с улучшенным поведением и позволяет приложению выбирать наиболее подходящий диспетчер.

DragManager

Этот объект обрабатывает захват ввода, предоставляет вспомогательные методы и сохраняет параметры перетаскивания и стыковки.

Сомнительно, чтобы объекту перетаскивания не разрешалось обрабатывать ввод пользователя. Это делает систему более безопасной, но не позволяет приложениям расширять пользовательский интерфейс для определенных операций перетаскивания.

Для вспомогательных методов требуются разные вызовы, но это безвредно, если они вызываются только из компонентов LCL.

Более важным является хранение параметров перетаскивания в объекте DragManager, которые исчезают при установке другого диспетчера перетаскивания. Это фатально в случае списка зарегистрированных стыковочных узлов :-(

Исполнители механизма перетаскивания

Внутреннее использование разных классов для перетаскивания и стыковки допустимо, поскольку процедуры значительно различаются между операциями drag-drop и drag-dock. Тем не менее общие действия должны использовать общий код, например унаследован от общего базового класса.

Диаграмма последовательности сообщений или диаграмма последовательности UML

Предоставлено Tom Verhoeff

Что-то вроде линий (это для Delphi; просмотр с моноширинным шрифтом):

                  Main            Drag          (Optional)        Drag
  User            Event          Source           Drag           Target
 (Mouse)          Loop           Control          Object         Control
    =               =               =                               =
    |               |               |                               |
   1|--MouseDown--->|               |                               |
    |              2#--OnMouseDown->|                               |
    |               |              3#                               |
    |              4#<-BeginDrag()--#                               |
    |               #               #                               |
    |               |               |                               |
    |              5#--OnStartDrag->|                               |
    |               #              6#----Create---->=               |
    |               |               #               |               |
   7|--MouseMove--->|               |               |               |
    |              8#-----------------------------------OnDragOver->|
    |               #               |               |              9#
    |               #               |               |               #
    |               |               |               |               |
  10|---MouseUp---->|               |               |               |
    |             11#-----------------------------------OnDragDrop->|
    |               #               |               |             12#
    |               #               |               |               #
    |               #---OnEndDrag-->|               |               |
    |               |             13#----Destroy--->=               |
    |               |               |                               |


DragObject не является обязательным. Может быть несколько потенциальных целей. Цикл главного события определяет, какое событие и какому объекту отправлять, в зависимости от действия пользователя, положения курсора и текущего состояния графического интерфейса.

4 некоторым образом запускает 5 есть задержка в зависимости от Boolean-параметра Immediate в BeginDrag).

6 определяет местоположение MouseDown и может использовать это для принятия решения, что перетаскивается (через GetCursorPos, ScreenToClient, MouseToCell). Он может вызывать SetDragImage для визуализации перетаскиваемого объекта.

Цепочка 7, 8, 9 может повторяться; 9 указывает на приемлемость сброса компонента на эту цель через Boolean-параметр var Accepted.

12 вызывается только в том случае, если непосредственно предшествующий событию OnDragOver этой цели вернул Accepted = True; т.е. Accepted - предварительное условие 12.

13 получает параметр Target, чтобы узнать, где был принят брошенный компонент; Если Target=nil, то сброс компонента был отменен пользователем (MouseUp в недопустимом месте), а OnDragDrop не было выполнено; Если Target<>nil, то OnDragDrop уже был выполнен на Target. Таким образом, работа, связанная с фактическим сбросом, делится на OnDragDrop (для действий в целевом объекте) и OnEndDrag (для действий в источнике). Поскольку объект перетаскивания необходимо уничтожить, независимо от того, было ли перетаскивание успешным или отмененным, лучше всего вызывать Free (или Destroy) только в обработчике OnEndDrag.

В любой момент может быть активным только одно перетаскивание; Он либо завершается сбросом компоента, либо отменой сброса.

Есть много других вещей, которые нужно включить, но это основная часть. (Можно также посмотреть Delphi-Central или "Implementing drag-and-drop in controls".)

Надеюсь, это поможет. Tom

См. также