Sign in Join
Wiki copytemporary

Build custom dock manager/ru: what changed

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

Title: Build custom dock manager/ru

OldNewLine
1+## Введение
2+
3+VCL и LCL поддерживают базовую стыковку элементов управления. Для расширенного режима стыковки необходимо создать настраиваемый диспетчер стыковочного узла. Базовый абстрактный класс диспетче растыковочного узла TDockManager расположен в модуле Controls. Все настраиваемые менеджеры должны быть производными от этого класса. Базовая реализация TDockManager в LCL - это TDockTree, способная обрабатывать дерево стыковочных зон.
4+
5+Рекомендуемая литература: [[LCL Drag Dock]]
6+
7+## Возможные функции стыковки
8+
9+- Иерархия зоны стыковки
10+ - Один элемент
11+ - Линейный список
12+ - Дерево
13+ - Таблица
14+ - Привязанный макет
15+ - Еще одна сложная организация
16+- Пристыкованные вкладки
17+- Пристыковка нескольких форм к плавающему объединенному окну
18+- Темы
19+- Постоянство, хранение/загрузка состояния макета стыковки
20+- Пристыковка вкладок с автоматическим отображением/скрытием форм, кнопка с изображением булавки для переключения автоматического скрытия/отображения
21+- Пользовательские кнопки заголовка
22+- Создание макета по умолчанию прямо из кода, ручное управление
23+- Поддержка визуальных компонентов времени разработки, настройка формы стыковки
24+- Класс для глобальных операций пристыковки
25+
26+## Перетаскивание
27+
28+Пристыковка - это, по сути, особый случай действия перетаскивания для форм и подобных элементов управления. Поэтому основные события элементов управления аналогичны.
29+
30+События перетаскивания: `OnDragOver`, `OnDragDrop`, `OnStartDrag`, `OnEndDrag`. События стыковки: `OnDockOver`, `OnDockDrop`, `OnStartDock`, `OnEndDock`, `OnUnDock`.
31+
32+Перетаскиваемый объект представлен классом `TDragObject`. Этот класс расширен до `TDragControlObject`; затем это расширяется до `TDragDockObject` для поддержки функций стыковки. Весь процесс перетаскивания контролируется `TDragManager`, аналогично `TDockManager`, управляющему процессом закрепления.
33+
34+## Подсистема стыковки
35+
36+### Поддержка TControl
37+
38+``` pascal
39+TControl = class
40+ ...
41+ procedure DragDrop(Source: TObject; X,Y: Integer); virtual;
42+ procedure Dock(NewDockSite: TWinControl; ARect: TRect); virtual;
43+ function ManualDock(NewDockSite: TWinControl;
44+ DropControl: TControl = nil; ControlSide: TAlign = alNone;
45+ KeepDockSiteSize: Boolean = true): Boolean; virtual;
46+ function ManualFloat(TheScreenRect: TRect;
47+ KeepDockSiteSize: Boolean = true): Boolean; virtual;
48+
49+ property DockOrientation: TDockOrientation read FDockOrientation write FDockOrientation;
50+ property Floating: Boolean read GetFloating;
51+ property FloatingDockSiteClass: TWinControlClass read GetFloatingDockSiteClass
52+ write FFloatingDockSiteClass;
53+ property HostDockSite: TWinControl read FHostDockSite write SetHostDockSite;
54+ property LRDockWidth: Integer read GetLRDockWidth write FLRDockWidth;
55+ property TBDockHeight: Integer read GetTBDockHeight write FTBDockHeight;
56+ property UndockHeight: Integer read GetUndockHeight write FUndockHeight; // Высота, используемая при отстыковке
57+ property UndockWidth: Integer read GetUndockWidth write FUndockWidth; // Ширина, используемая при отстыковке
58+ ...
59+end;
60+```
61+
62+### Поддержка TWinControl
63+
64+``` pascal
65+TWinControl = class(TControl)
66+ ...
67+ property DockClientCount: Integer read GetDockClientCount;
68+ property DockClients[Index: Integer]: TControl read GetDockClients;
69+ property DockManager: TDockManager read FDockManager write SetDockManager;
70+ property DockSite: Boolean read FDockSite write SetDockSite default False;
71+ property OnUnDock: TUnDockEvent read FOnUnDock write FOnUnDock;
72+ property UseDockManager: Boolean read FUseDockManager
73+ write SetUseDockManager default False;
74+ property VisibleDockClientCount: Integer read GetVisibleDockClientCount;
75+ procedure DockDrop(DragDockObject: TDragDockObject; X, Y: Integer); virtual;
76+ ...
77+end;
78+```
79+
80+## Реализация базового настраиваемого DockManager
81+
82+Наш собственный менеджер можно получить из TDockTree по умолчанию. Но для создания полностью настраиваемого менеджера родительский класс должен быть TDockManager. Все методы родительского класса следует переопределить.
83+
84+- Для базовой демонстрации диспетчер стыковочного узла должен уметь управлять одним элементом управления. Позже этот класс может быть расширен.
85+
86+Вот базовый прототип, основанный на виртуальных методах TDockManager.
87+
88+``` pascal
89+TCustomDockManager = class(TDockManager)
90+ constructor Create(ADockSite: TWinControl); override;
91+ procedure BeginUpdate; override;
92+ procedure EndUpdate; override;
93+ procedure GetControlBounds(Control: TControl;
94+ out AControlBounds: TRect); override;
95+ function GetDockEdge(ADockObject: TDragDockObject): boolean; override;
96+ procedure InsertControl(ADockObject: TDragDockObject); override; overload;
97+ procedure InsertControl(Control: TControl; InsertAt: TAlign;
98+ DropCtl: TControl); override; overload;
99+ procedure LoadFromStream(Stream: TStream); override;
100+ procedure PaintSite(DC: HDC); override;
101+ procedure MessageHandler(Sender: TControl; var Message: TLMessage); override;
102+ procedure PositionDockRect(ADockObject: TDragDockObject); override; overload;
103+ procedure PositionDockRect(Client, DropCtl: TControl; DropAlign: TAlign;
104+ var DockRect: TRect); override; overload;
105+ procedure RemoveControl(Control: TControl); override;
106+ procedure ResetBounds(Force: Boolean); override;
107+ procedure SaveToStream(Stream: TStream); override;
108+ procedure SetReplacingControl(Control: TControl); override;
109+ function AutoFreeByControl: Boolean; override;
110+end;
111+```
112+
113+- DockManager должен знать, к какому TWinControl принадлежит. Итак, менеджеру необходимо поле FDockSite типа TWinControl, которое назначается конструктором.
114+
115+``` pascal
116+private
117+ FDockSite: TWinControl;
118+```
119+
120+- Теперь мы можем реализовать метод PositionDockRect, который определяет размер фрейма стыковочного узла в соответствии с размером DockSite.
121+
122+``` pascal
123+procedure TCustomDockManager.PositionDockRect(Client, DropCtl: TControl; DropAlign: TAlign;
124+ var DockRect: TRect); override; overload;
125+begin
126+ DockRect := Rect(0, 0, FDockSite.ClientWidth, FDockSite.ClientHeight);
127+end;
128+```
129+
130+- Чтобы сделать диспетчер стыковочного узла по умолчанию для всех пристыковываемых элементов управления, вставьте раздел инициализации в конец модуля. Затем простое включение модуля менеджера стыковочного узла в модуль основной формы настроит вашу систему стыковки.
131+
132+``` pascal
133+initialization
134+ DefaultDockManagerClass := TCustomDockManager;
135+```
136+
137+- Если размер стыковочного узла изменяется, выполняется метод `ResetBounds`. Затем мы можем использовать его для размещения стыкуемого элемента управления на стыковочном узле. Нам нужно выделить место поверх элемента управления для граббера(захватчика) стыкуемого элемента.
138+
139+``` pascal
140+const
141+ GrabberSize = 18;
142+
143+procedure TCustomDockManager.ResetBounds(Force: Boolean);
144+var
145+ I: Integer;
146+ Control: TControl;
147+ R: TRect;
148+begin
149+ for I := 0 to FDockSite.ControlCount - 1 do
150+ begin
151+ Control := FDockSite.Controls[I];
152+ if Control.Visible and (Control.HostDockSite = FDockSite) then
153+ begin
154+ R := Control.BoundsRect;
155+ Control.SetBounds(0, GrabberSize, FDockSite.Width - Control.Left,
156+ FDockSite.Height - Control.Top);
157+ end;
158+ end;
159+end;
160+```
161+
162+- Теперь нам нужно нарисовать граббер с именем элемента поверх стыковочного узла.
163+
164+``` pascal
165+procedure TCustomDockManager.DrawGrabber(Canvas: TControlCanvas; AControl: TControl);
166+begin
167+ with Canvas do begin
168+ Brush.Color := clBtnFace;
169+ Pen.Color := clBlack;
170+ FillRect(0, 0, AControl.Width, GrabberSize);
171+ Rectangle(1, 1, AControl.Width - 1, GrabberSize - 1);
172+ TextOut(6, 2, AControl.Caption);
173+ end;
174+end;
175+```
176+
177+Для отрисовки стыковочного узла холст использует метод `PaintSite`, поэтому нам нужно его реализовать. Должны быть отрисованы только видимые элементы управления, принадлежащие стыковочному узлу.
178+
179+``` pascal
180+procedure TCustomDockManager.PaintSite(DC: HDC);
181+var
182+ Canvas: TControlCanvas;
183+ Control: TControl;
184+ I: Integer;
185+ R: TRect;
186+begin
187+ Canvas := TControlCanvas.Create;
188+ try
189+ Canvas.Control := FDockSite;
190+ Canvas.Lock;
191+ try
192+ Canvas.Handle := DC;
193+ try
194+ for I := 0 to FDockSite.ControlCount - 1 do
195+ begin
196+ Control := FDockSite.Controls[I];
197+ if Control.Visible and (Control.HostDockSite = FDockSite) then
198+ begin
199+ R := Control.BoundsRect;
200+ Control.SetBounds(0, GrabberSize, FDockSite.Width - Control.Left,
201+ FDockSite.Height - Control.Top);
202+ Canvas.FillRect(R);
203+ DrawGrabber(Canvas, Control);
204+ end;
205+ end;
206+ finally
207+ Canvas.Handle := 0;
208+ end;
209+ finally
210+ Canvas.Unlock;
211+ end;
212+ finally
213+ Canvas.Free;
214+ end;
215+end;
216+```
217+
218+*продолжение следует...*
219+
220+## See also
221+
222+- [[LCL Drag Dock/ru|LCL Drag Dock]]
223+
224+---
225+*From the Free Pascal wiki, page "Build custom dock manager/ru", as it stood on 2022-01-02 after a change by FTurtle, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history