LCL Drag Dock/ru
│
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
.