Difference between revisions of "LCL Drag Dock/ru"

From Lazarus wiki
Jump to navigationJump to search
Line 57: Line 57:
 
Унаследованный DragTarget - это целевой стыковочный сайт в стыковке. Добавленные свойства <code>DropOnControl</code> и <code>DropAlign</code> указывают, где и как перетаскиваемый элемент управления должен располагаться относительно уже закрепленного элемента управления.
 
Унаследованный DragTarget - это целевой стыковочный сайт в стыковке. Добавленные свойства <code>DropOnControl</code> и <code>DropAlign</code> указывают, где и как перетаскиваемый элемент управления должен располагаться относительно уже закрепленного элемента управления.
  
=== Helpers ===
+
=== Помощники (Helpers) ===
A bunch of helper methods and fields has been introduced into the TControl and TWinControl classes, for the internal management of docked controls and the customization of the docking process. Most of these helpers play a dedicated role in the process of docking, and will be described in the work flow below.
+
В классы TControl и TWinControl добавлен ряд вспомогательных методов и полей для внутреннего управления закрепленными элементами управления и настройки процесса стыковки. Большинство этих помощников играют особую роль в процессе стыковки и будут описаны в рабочем процессе ниже.
  
(placeholder for descriptions of general items)
+
(место для описания общих элементов)
  
 
== Work Flow ==
 
== Work Flow ==

Revision as of 08:42, 15 January 2021

English (en) русский (ru)

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

Операции

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

DragInit

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

DragTo

На ходу нужно сделать пару шагов:

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

DragDone

При бросании элемента управления стыковка может быть заблокирована по нескольким причинам. Трудно объяснить, почему стыковочный узел должен отказывать в откреплении перетаскиваемого элемента управления, когда он, наконец, отбрасывается. Возможны следующие случаи:

  • Элемент управления пристыкован к новому сайту док-станции.
  • Перетаскиваемый элемент управления становится плавающим
    • как есть, если он уже был плавающим, или когда он может стать самостоятельно плавающим это TWinControl),
    • или завернутый в новую плавающую форму.
  • Вся операция прервана.

Обзор стыковочных элементов

Помимо drag-drop участников, на сцену выходят еще несколько участников:

  • Стыковочные узлы (Dock sites)
  • Стыковочные прямоугольники в качестве визуальной обратной связи
  • Диспетчеры стыковки (Dock managers)

Введение диспетчеров стыковки создало кучу новых проблем с плохой интеграцией в модель неуправляемой док-станции. Давайте пока проигнорируем "управляемую" стыковку и рассмотрим только "неуправляемую" стыковку.

Прикрепляемый элемент (Dock Source)

Элемент управления или форму можно сделать прикрепляемыми, установив для его свойства DragKind значение dkDock.

Стыковочные узлы (Dock Sites)

Для стыковки требуются специальные целевые зоны сброса [для прикрепляемого элемента], называемые стыковочными узлами. TWinControl становится такой стыковочной целью, при установке его свойства DockSite в значение True.

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

Плавающие стыковочные узлы

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

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

Стыковочный прямоугольник (DockRect)

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

Когда не удается найти принимающий стыковочный узел или когда пользователь запрещает перетаскивание, удерживая клавишу Ctrl, DockRect отражает плавающий размер и положение перетаскиваемого элемента управления. Плавающий размер инициализируется исходным размером перетаскиваемого элемента управления и обновляется при перемещении элемента управления. В противном случае стыковочный узел может определить размещение и размер брошенного элемента управления.

Реализация Delphi запоминает координаты нарисованного DockRect в EraseDockRect, так что старый фрейм может быть удален с экрана с помощью XOR-рисования, когда это необходимо.

Стыковочный объект (DockObject)

В операции стыковки используется специальный DockObject, производный от TDragObject. Непонятно, почему этот класс вводит новые методы вместо того, чтобы переопределять существующие виртуальные методы для различной визуальной обратной связи.

Унаследованный DragTarget - это целевой стыковочный сайт в стыковке. Добавленные свойства DropOnControl и DropAlign указывают, где и как перетаскиваемый элемент управления должен располагаться относительно уже закрепленного элемента управления.

Помощники (Helpers)

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

(место для описания общих элементов)

Work Flow

This is the detailed description of the required actions, before, during, and after a control is docked.

Prerequisites

A control is made dockable by setting its DragKind to dkDock. Nothing else is required for a dock source.

A global list holds all dock sites in an application. Dock sites are added and removed from this list, when their DockSite property changes.

RegisterDockSite

Dock sites can be covered partially or entirely by other forms, but shall act as docking targets in any case. This procedure registers and unregisters controls in an application as dock sites, so that even hidden candidates can be found while dragging a dock source over the screen.

GetSiteInfo

The TWinControl.GetSiteInfo method returns an influence (catching) rectangle, associated with the dock site. This convention allows to e.g. hide empty dock sites in the GUI, without making them unreachable for dropping. The default implementation returns the visible control extent, increased by an certain amount in either direction.

DragInit

Docking starts automatically when a control has DragKind=dkDock and DragMode=dmAutomatic, and the left mouse button is pressed on that control. A TControl provides a BeginAutoDrag method for this case, and a BeginDrag method for an programmatical start when DragMode=dmManual.

A DockObject is created, either by the OnDockStart handler of the source control (TControl.DoStartDock), or by the drag manager when no such object was provided. The DockRect is initialized to the control's rectangle. For a convenient position of the DockRect during moves, the offset of the mouse pointer to the DockRect is remembered.

The input capture is moved to the control, or to it's TWinControl parent, when the control cannot receive system (Windows) messages.

When dragging shall start immediately, DragTo is invoked.

DragTo

DragTo checks for an delayed start, and does nothing when the mouse move threshold was not yet reached.

Otherwise a drag target is searched. When the target changes, dmDragLeave and dmDragEnter are sent to the target by DoDragOver, as CM_DRAG messages.

A dmDragMove message is sent the same way and is handled in TWinControl.DockOver, which positions the DockRect and invokes the OnDockOver handler. The result indicates whether a drop will be accepted, and the visual feedback is updated accordingly.

Delphi Woes

The next step in the Delphi implementation is bogus. When dropping is denied, the DockRect is reset to the floating position and extent of the dragged control, what already should have been done before. In addition a DropOnControl is searched in the dock site, and the DropAlign is determined. Neither of these values is reflected in the previously determined DockRect, so that the intended effect becomes visible at best (if ever) after the next mouse move. When no DropOnControl is found, what's very likely in an unmanaged dock site, the DockRect will cover the entire dock site - what's not very meaningful. When a DropOnControl is found, the meaning of the DropAlign is undefined; the Delphi implementation uses this value to adjust the DockRect to the upper (left...) half of the DropOnControl, but such a placement of the dropped control only can make sense, when the DropOnControl itself is shrinked accordingly. In a managed dock site the DockManager can adjust the DockRect, and later can fit the affected controls into the indicated boundaries, but he has no chance to determine a DropOnControl or DropAlign himself. Thus the meaning of DropOnControl and DropAlign is questionable, both for managed and unmanaged dock sites.

The best solution will omit that step entirely, and let the dock site and event handlers manage the visual feedback and final drop, or leaves all that to the installed DockManager.

DragDone

When dragging is finished, either by a cancel or successful drop, the capture is released and the visual feedback is removed from the screen. The further procedure depends on the success of the operation.

Manual Dock

The TControl.ManualDock method bypasses all user interaction, and docks the control into a dock site or makes it float (TControl.ManualFloat). If required by further processing of the request, a DockObject has to be created and initialized from the parameters.

Drop into DockSite

In a first step the control is logically moved into the new dock site, unless it's re-docked within the same dock site. It is removed from its old dock site's control list and moved into the new site's list of docked controls, and its Parent is adjusted accordingly.

Finally the control is placed within its new Parent, according to the last DockRect. In a managed dock site the DockManager can rearrange the docked controls as required.

Drop Nowhere

The dragged control becomes floating. A form can float for itself, but for convenience all controls can be wrapped into an dedicated floating form container (default: FloatingDockSiteClass = TCustomDockForm).

A CM_FLOAT message is sent to the source control. When the control is already floating, it repositions it's Parent, and everything is done.

Otherwise the the control sets the DragTarget to a newly created floating dock site, and subsequently is docked into that site as on any other target site.

Don't Drop

The source control receives a DragCanceled notification, that's all.

Events

The following events occur during a docking operation:

DragInit

The StartDrag/Dock handler can return a custom Drag/DockObject.

DragTo

When docking, the registered dock sites receive a GetSiteInfo event. The handlers can provide the influence rectangle, and whether a drop is acceptable.

When the target changes, the old and new targets receive a Drag/DockOver event of state dmDragEnter/Leave.

In all cases a Drag/DockOver event of state dmDragMove occurs. The handler can reject an drop, and can adjust the visual feedback in the given drag/dock object.

DragDone

A final Drag/DockMove of state dmDragLeave is sent to the target. This is the first event in a programmatical drop (TControl.ManualDock). The state better should be dmDragMove, but the Delphi implementation uses dmDragLeave.

When docking, an UnDock event allows the source site to reject (cancel) the drop. The event handler, dock site and docking manager are asked in sequence to reject or perform the appropriate internal action.

When the drop is not rejected, a Drag/DockDrop event occurs.

In all cases the source receives an EndDrag/Dock event.