Sign in Join
Wiki copytemporary

Build custom dock manager: what changed

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

Title: Build custom dock manager

OldNewLine
1+## Introduction
2+
3+VCL and LCL support basic docking of controls. For advanced docking behavior, a custom dock manager has to be created. A basic abstract dock manager class TDockManager is placed in Controls unit. All custom managers have to be derived from this class. A basic LCL implementation of TDockManager is TDockTree which is capable of handling tree of dock zones.
4+
5+Recommended reading: [[LCL Drag Dock]]
6+
7+## Possible docking features
8+
9+- Docking zone hierarchy
10+ - One item
11+ - Linear list
12+ - Tree
13+ - Table
14+ - Anchored layout
15+ - Another complex organization
16+- Tabbed docking
17+- Docking multiple forms to floating conjoined window
18+- Themes
19+- Persistence, store/load docking layout
20+- Tabbed docking with autoshow/autohide forms, pin button to switch autohide/visible
21+- Custom header buttons
22+- Building default layout directly from code, manual management
23+- Supporting visual design-time components, docking customize form
24+- Class for global docking operations
25+
26+## Dragging
27+
28+Docking is a basically special case of Drag & Drop action for forms and similar controls. Therefore basic events of controls are similar.
29+
30+Dragging events: OnDragOver, OnDragDrop, OnStartDrag, OnEndDrag Docking events: OnDockOver, OnDockDrop, OnStartDock, OnEndDock, OnUnDock
31+
32+The dragged object is represented by the TDragObject class. This class is further extended to TDragControlObject; this is then extended to TDragDockObject to support docking features. The whole dragging process is controlled by TDragManager, similarly to TDockManager controlling the docking process.
33+
34+## Docking subsystem
35+
36+### TControl support
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; // Height used when undocked
57+ property UndockWidth: Integer read GetUndockWidth write FUndockWidth; // Width used when undocked
58+ ...
59+end;
60+```
61+
62+### TWinControl support
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+## Implementing basic custom DockManager
81+
82+It is possible to derive our custom manager from default TDockTree. But for purposes of creating a completely custom manager, the parent class has to be TDockManager. All methods from the parent class should be overridden.
83+
84+- For basic demonstration, the dock manager should be able to manage one control. That class can be extended later.
85+
86+Here is a basic prototype based on TDockManager virtual methods.
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 has to know to which TWinControl belong. So manager needs field FDockSite of type TWinControl which is assigned by constructor.
114+
115+``` pascal
116+private
117+ FDockSite: TWinControl;
118+```
119+
120+- Now we can implement a method PositionDockRect which determines dock frame size according to DockSite size.
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+- To make dock manager default for all dockable controls, insert an initialization section to the end of the unit. Then simple inclusion of the dock manager unit into main form unit will setup your docking system.
131+
132+``` pascal
133+initialization
134+ DefaultDockManagerClass := TCustomDockManager;
135+```
136+
137+- If dock site is resized, the ResetBounds method is executed. Then we can use it to position the docked control on dock site. We have to allocate some space on top of the control for the dock grabber.
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+- Now we need to draw a grabber with control Name on top of the dock site.
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+For painting the dock site, the canvas responds to the PaintSite method, so we need to implement it. Only visible controls belonging to the dock site should be painted.
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+*to be continued...*
219+
220+## See also
221+
222+- [[LCL Drag Dock]]
223+
224+---
225+*From the Free Pascal wiki, page "Build custom dock manager", 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