Difference between revisions of "LCL Drag Dock/ru"

From Free Pascal wiki
Jump to navigationJump to search
 
(3 intermediate revisions by the same user not shown)
Line 17: Line 17:
  
 
=== DragDone ===
 
=== DragDone ===
При бросании элемента управления стыковка может быть заблокирована по нескольким причинам. Трудно объяснить, почему стыковочный узел должен отказывать в откреплении перетаскиваемого элемента управления, когда он, наконец, отбрасывается. Возможны следующие случаи:
+
При бросании элемента управления стыковка может быть заблокирована по нескольким причинам. Трудно объяснить, почему стыковочный узел должен отказывать в откреплении перетаскиваемого элемента управления, когда он, наконец, стаскивается. Возможны следующие случаи:
 
* Элемент управления пристыкован к новому сайту док-станции.
 
* Элемент управления пристыкован к новому сайту док-станции.
 
* Перетаскиваемый элемент управления становится плавающим
 
* Перетаскиваемый элемент управления становится плавающим
Line 86: Line 86:
  
 
=== DragTo ===
 
=== DragTo ===
DragTo checks for an delayed start, and does nothing when the mouse move threshold was not yet reached.
+
<code>DragTo</code> проверяет наличие отложенного запуска и ничего не делает, если порог перемещения мыши еще не достигнут.
  
Otherwise a drag target is searched. When the target changes, dmDragLeave and dmDragEnter are sent to the target by DoDragOver, as CM_DRAG messages.
+
В противном случае ищется цель перетаскивания. Когда цель изменяется, <code>DoDragOver</code> отправляет в цель <code>dmDragLeave</code> и <code>dmDragEnter</code> в виде сообщений <code>CM_DRAG</code>.
  
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.
+
Сообщение <code>dmDragMove</code> отправляется таким же образом и обрабатывается в <code>TWinControl.DockOver</code>, который позиционирует <code>DockRect</code> и вызывает обработчик <code>OnDockOver</code>. Результат указывает, будет ли принято решение о сбросе, и соответственно обновляется визуальная обратная связь.
  
==== Delphi Woes ====
+
==== Горести Delphi ====
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.
+
Следующий шаг в реализации Delphi - фиктивный. Когда cброс(dropping) отклонен, <code>DockRect</code> сбрасывается в плавающее положение и размер перетаскиваемого элемента управления, что уже должно было быть сделано раньше. Кроме того, на стыковочном узле выполняется поиск <code>DropOnControl</code> и определяется <code>DropAlign</code>. Ни одно из этих значений не отражается в ранее определенном <code>DockRect</code>, поэтому предполагаемый эффект становится видимым в лучшем случае (если вообще когда-либо) после следующего движения мыши. Когда <code>DropOnControl</code> не найден, что весьма вероятно на неуправляемом стыковочном узле, <code>DockRect</code> будет охватывать весь стыковочный узел - что не очень важно. Когда <code>DropOnControl</code> найден, значение <code>DropAlign</code> не определено; реализация Delphi использует это значение для настройки <code>DockRect</code> на верхнюю (левую ...) половину <code>DropOnControl</code>, но такое размещение отброшенного элемента управления может иметь смысл только тогда, когда сам <code>DropOnControl</code> соответствующим образом сокращается. В управляемом стыковочном узле DockManager может настраивать <code>DockRect</code>, а затем может уместить затронутые элементы управления в указанные границы, но у него нет возможности самому определить <code>DropOnControl</code> или <code>DropAlign</code>. Таким образом, значение <code>DropOnControl</code> и <code>DropAlign</code> вызывает сомнения как для управляемых, так и для неуправляемых стыковочных узлов.
  
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.
+
При лучшем решении этот шаг будет полностью опущен, а обработчики событий и стыковочного узла будут управлять визуальной обратной связью и окончательным сбросом или оставят все это установленному DockManager.
  
 
=== DragDone ===
 
=== 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.
+
Метод <code>TControl.ManualDock</code> обходит все действия пользователя и закрепляет элемент управления на стыковочном узле или делает его плавающим (<code>TControl.ManualFloat</code>). Если требуется для дальнейшей обработки запроса, должен быть создан и инициализирован <code>DockObject</code> из параметров.
  
==== 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.  
+
Наконец, в соответствии с последним <code>DockRect</code>, элемент управления помещается в новый родительский элемент.
In a managed dock site the DockManager can rearrange the docked controls as required.
+
В управляемой стыковочном узле DockManager может при необходимости переставлять закрепленные элементы управления.
  
==== 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).
+
Форма может перемещаться сама по себе, но для удобства все элементы управления могут быть заключены в специальный контейнер плавающей формы (по умолчанию: <code>FloatingDockSiteClass=CustomDockForm</code>).
  
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.
+
Сообщение <code>CM_FLOAT</code> отправляется в исходный код элемента управления. Когда элемент управления уже плавает, он перемещает свой родительский элемент, и все готово.
  
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.
+
В противном случае элемент управления устанавливает <code>DragTarget</code> на вновь созданный стыковочный узел, а затем закрепляется на этом узле, как и на любом другом целевом узле.
  
==== Don't Drop ====
+
==== Отмена сброса ====
The source control receives a DragCanceled notification, that's all.
+
Исходный элемент управления получает уведомление <code>DragCanceled</code>, вот и все.
  
== Events ==
+
== События ==
The following events occur during a docking operation:
+
Во время стыковки происходят следующие события:
  
 
=== DragInit ===
 
=== DragInit ===
The '''StartDrag/Dock''' handler can return a custom Drag/DockObject.
+
Обработчик <code>StartDrag</code>/<code>Dock</code> может возвращать пользовательский <code>Drag</code>/<code>DockObject</code>.
  
 
=== DragTo ===
 
=== DragTo ===
When docking, the registered dock sites receive a '''GetSiteInfo''' event. The handlers can provide the influence rectangle, and whether a drop is acceptable.
+
При стыковке зарегистрированные стыковочные узлы получают событие <code>GetSiteInfo</code>. Обработчики могут предоставить прямоугольник влияния и указать, допустим ли сброс.
  
When the target changes, the old and new targets receive a '''Drag/DockOver''' event of state ''dmDragEnter/Leave''.
+
Когда цель изменяется, старая и новая цели получают событие <code>Drag</code>/<code>DockOver</code> с состоянием <code>dmDragEnter</code>/<code>Leave</code>.
  
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.
+
Во всех случаях происходит событие <code>Drag</code>/<code>DockOver</code> в состоянии <code>dmDragMove</code>. Обработчик может отклонить перетаскивание и может настроить визуальную обратную связь в данном объекте drag/dock.
  
 
=== DragDone ===
 
=== 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.
+
Конечное значение <code>Drag</code>/<code>DockMove</code> состояния <code>dmDragLeave</code> отправляется цели. Это первое событие в программном сбросе элемента управления (<code>TControl.ManualDock</code>). Состояние лучше должно быть <code>dmDragMove</code>, но реализация Delphi использует <code>dmDragLeave</code>.
  
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.
+
При стыковке событие <code>UnDock</code> позволяет исходному сайту отклонить (отменить) сброс. Обработчику событий, стыковочному узлу и стыковочному диспетчеру предлагается последовательно отклонить или выполнить соответствующее внутреннее действие.
  
When the drop is not rejected, a '''Drag/DockDrop''' event occurs.
+
Если перетаскивание не отклоняется, возникает событие <code>Drag</code>/<code>DockDrop</code>.
  
In all cases the source receives an '''EndDrag/Dock''' event.
+
Во всех случаях источник получает событие <code>EndDrag<code>/<code>Dock</code>.

Latest revision as of 16:12, 18 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 добавлен ряд вспомогательных методов и полей для внутреннего управления закрепленными элементами управления и настройки процесса стыковки. Большинство этих помощников играют особую роль в процессе стыковки и будут описаны в рабочем процессе ниже.

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

Механика процесса

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

Предпосылки

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

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

RegisterDockSite

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

GetSiteInfo

Метод TWinControl.GetSiteInfo возвращает прямоугольник влияния (захвата), связанный со стыковочным узлом. Это соглашение позволяет, например, скрыть стыковочные узлы в графическом интерфейсе, без превращения их в недоступные для удаления. Реализация по умолчанию возвращает видимый размер элемента управления, увеличенный на определенную величину в любом направлении.

DragInit

Стыковка запускается автоматически, когда для элемента управления задано значение DragKind=dkDock и DragMode=dmAutomatic, а на этом элементе управления нажата левая кнопка мыши. TControl предоставляет метод BeginAutoDrag для этого случая и метод BeginDrag для запуска программной стыковки, когда DragMode=dmManual.

DockObject создается либо обработчиком OnDockStart исходного элемента управления (TControl.DoStartDock), либо диспетчером перетаскивания, если такой объект не был предоставлен. DockRect инициализируется прямоугольником элемента управления. Для удобного положения DockRect во время перемещений запоминается смещение указателя мыши на DockRect.

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

Когда перетаскивание должно начаться немедленно, вызывается DragTo.

DragTo

DragTo проверяет наличие отложенного запуска и ничего не делает, если порог перемещения мыши еще не достигнут.

В противном случае ищется цель перетаскивания. Когда цель изменяется, DoDragOver отправляет в цель dmDragLeave и dmDragEnter в виде сообщений CM_DRAG.

Сообщение dmDragMove отправляется таким же образом и обрабатывается в TWinControl.DockOver, который позиционирует DockRect и вызывает обработчик OnDockOver. Результат указывает, будет ли принято решение о сбросе, и соответственно обновляется визуальная обратная связь.

Горести Delphi

Следующий шаг в реализации Delphi - фиктивный. Когда cброс(dropping) отклонен, DockRect сбрасывается в плавающее положение и размер перетаскиваемого элемента управления, что уже должно было быть сделано раньше. Кроме того, на стыковочном узле выполняется поиск DropOnControl и определяется DropAlign. Ни одно из этих значений не отражается в ранее определенном DockRect, поэтому предполагаемый эффект становится видимым в лучшем случае (если вообще когда-либо) после следующего движения мыши. Когда DropOnControl не найден, что весьма вероятно на неуправляемом стыковочном узле, DockRect будет охватывать весь стыковочный узел - что не очень важно. Когда DropOnControl найден, значение DropAlign не определено; реализация Delphi использует это значение для настройки DockRect на верхнюю (левую ...) половину DropOnControl, но такое размещение отброшенного элемента управления может иметь смысл только тогда, когда сам DropOnControl соответствующим образом сокращается. В управляемом стыковочном узле DockManager может настраивать DockRect, а затем может уместить затронутые элементы управления в указанные границы, но у него нет возможности самому определить DropOnControl или DropAlign. Таким образом, значение DropOnControl и DropAlign вызывает сомнения как для управляемых, так и для неуправляемых стыковочных узлов.

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

DragDone

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

Ручная стыковка

Метод TControl.ManualDock обходит все действия пользователя и закрепляет элемент управления на стыковочном узле или делает его плавающим (TControl.ManualFloat). Если требуется для дальнейшей обработки запроса, должен быть создан и инициализирован DockObject из параметров.

Сброс внутри стыковочного узла

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

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

Сброс в никуда

Перетаскиваемый элемент управления становится плавающим. Форма может перемещаться сама по себе, но для удобства все элементы управления могут быть заключены в специальный контейнер плавающей формы (по умолчанию: FloatingDockSiteClass=CustomDockForm).

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

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

Отмена сброса

Исходный элемент управления получает уведомление DragCanceled, вот и все.

События

Во время стыковки происходят следующие события:

DragInit

Обработчик StartDrag/Dock может возвращать пользовательский Drag/DockObject.

DragTo

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

Когда цель изменяется, старая и новая цели получают событие Drag/DockOver с состоянием dmDragEnter/Leave.

Во всех случаях происходит событие Drag/DockOver в состоянии dmDragMove. Обработчик может отклонить перетаскивание и может настроить визуальную обратную связь в данном объекте drag/dock.

DragDone

Конечное значение Drag/DockMove состояния dmDragLeave отправляется цели. Это первое событие в программном сбросе элемента управления (TControl.ManualDock). Состояние лучше должно быть dmDragMove, но реализация Delphi использует dmDragLeave.

При стыковке событие UnDock позволяет исходному сайту отклонить (отменить) сброс. Обработчику событий, стыковочному узлу и стыковочному диспетчеру предлагается последовательно отклонить или выполнить соответствующее внутреннее действие.

Если перетаскивание не отклоняется, возникает событие Drag/DockDrop.

Во всех случаях источник получает событие EndDrag/Dock.