Sign in Join
Wiki copytemporary

Autosize / Layout: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Overview
22
33The [[LCL]] can automatically alter a control's size and position so it adapts to changes in font, theme and text or other content. If you want to run your program on several platforms, or if your captions are available in several languages, your controls need to adapt correctly to their environment. The [[LCL]] allows you not only to make a quick first design (moving controls around on a form using the mouse) but also later to set several key properties which will make the controls automatically adapt subsequently to changed content etc.
44
55- Fixed design: this is the default when placing a control in the Designer. The control's position is fixed relative to its parent. The control's size and position (Left, Top) is fully adjustable by the programmer. You can move the control around with the mouse, and resize it freely.
66- Aligned: aligned controls fill up the remaining parental space at the top, bottom, left, or right, or fill the entire remaining space.
77- Anchored: you can anchor a control's sides (left, top, right, bottom) to its parent or to another control. Anchoring means: the [[LCL]] will try to maintain the same distance from the anchor point.
88- Layout: controls can be automatically aligned in rows and columns (e.g. TRadioGroup)
99- Custom via OnResize: you can align controls yourself in code using the OnResize and OnChangeBounds events.
1010- Custom controls: writing your own controls, you can override nearly every [[LCL]] behaviour you want.
1111
1212### Precedence rules
1313
14141. Constraints
15152. Align
16163. Anchors
17174. ChildSizing.Layout
18185. AutoSize
19196. OnResize, OnChangeBounds - however, if you set bounds that conflict with the rules above you will create an endless loop
2020
2121### Common Properties
2222
2323Several important properties can be changed to configure autosizing:
2424
2525- Left, Top, Width, Height
2626- AutoSize: autosize instructs the [[LCL]] to automatically resize the Width and Height of a control
2727- Anchors: lets you create dependencies, for example to anchor a ComboBox to the right side of a Label.
2828- Align
2929- Constraints: lets you set a minimum and maximum for the Width and Height
3030- BorderSpacing: lets you set the spacing between anchored controls
3131- ChildSizing: lets you set the layout and spacing of child controls
3232
3333The internals of the algorithms are explained here: [[LCL AutoSizing]].
3434
3535## Fixed design
3636
3737Fixed design is the default. The Anchor's property is set to \[akLeft,akTop\], which means the values of Left and Top are not changed by the [[LCL]]. If AutoSize is false, the [[LCL]] does not alter Width or Height. If AutoSize is true, the Width and Height are changed to fit the content. For example, TLabel.AutoSize defaults to true and thus changing its Caption will resize the label to accommodate the changed wording. TButton.AutoSize defaults to false, so changing a button's Caption does not resize the button. When you set Button.AutoSize to true the Button will shrink or enlarge every time the button's Caption or its font (or theme) changes. Note, this change does not always happen immediately. For example during FormCreate all autosizing is suspended. At any time you can change the "Left", "Top", "Width", or the "Height" property.
3838
3939## AutoSize
4040
4141`AutoSize` is a boolean property found in many classes; it permits the size of a control to be adjusted automatically to accommodate differences in the text or graphic contained therein, and allows most efficient use of available space. This is a crucial mechanism to create cross platform forms.
4242
4343Normally AutoSize=true does two things for a visible control:
4444
4545- If possible it resizes the control to the preferred size. For example a TButton's Width and Height is resized to fit the caption, while a TEdit is only resized in Height. A TEdit's Width is not autosized. You can change the Width of a TEdit. (see GetPreferredSize).
4646- It moves all fixed positioned child controls by the same amount, so that the leftmost child control is moved to Left=0 (depends on BorderSpacing) and the topmost child control is moved to Top=0.
4747
4848### Lazarus vs Delphi
4949
5050- Delphi's AutoSize happens only when certain properties change, for example when the Font of a TLabel is changed. The [[LCL]] AutoSize is always active. Delphi allows one to change the size of a Label which has AutoSize=true, the Lazarus control does not.
5151- Delphi's hidden controls are not autosized.
5252- With Delphi, changing the size of a control does not resize/move anchored child controls during loading, but a constructor is normally used to make the desired effect. When using akRight,akBottom anchors, set AnchorSides and BorderSpacing to keep the distance correct.
5353
5454### AutoSize and resizing the control
5555
5656With AutoSize=false buttons are given a default fixed size.
5757
5858![](/wiki-images/1/1f/Autosize1.png)
5959
6060When setting AutoSize=true for each button, the buttons are enlarged (or shrunk) to fit the text and theme frame.
6161
6262![](/wiki-images/7/75/Autosize_on.png)
6363
6464AutoSize does not shrink to the smallest possible size as you can see in the OK Button. It uses the GetPreferredSize method of the control, which calls the CalculatePreferredSize method. The default TWinControl implementation queries the widget set, which might have a preferred width or height. Each control can override the CalculatePreferredSize method. For example TImage overrides it and returns the size of the Picture. If no preferred width (height) is available the returned value is 0 and the [[LCL]] will keep the Width (Height) (unless the ControlStyle flag csAutoSize0x0 is set, which is currently only used by TPanel).
6565
6666A TWinControl computes the size of all its child controls and uses that to compute its preferred size.
6767
6868When a control is anchored to both left and right its *width is fixed*. For example with *Align=alTop* the control is anchored to left and right and follows the width of the Parent. If *Parent.AutoSize* is true, then the *Parent* will use the preferred width of the control to compute its own preferred width, and thus the control will be resized to its preferred width. See [[Autosize / Layout|Align and AutoSize]]. If no preferred width is available, then the last set bounds are used (**BaseBounds** or **ReadBounds**). If no bounds were set, the **GetControlClassDefaultSize** method is used. Same for **Height** and anchoring to top and bottom.
6969
7070Constraints are always applied and have precedence.
7171
7272### AutoSize and moving child controls
7373
7474When AutoSize=false you can place and move controls freely:
7575
7676![](/wiki-images/7/75/Autosize_on.png)
7777
7878When AutoSize=true child controls with fixed positions are moved to fit.
7979
8080![](/wiki-images/d/d9/Autosize_panel_on.png)
8181
8282Both buttons on the panel were moved by the same amount to top and left, so that there is no space left to the left and top. If the buttons would have BorderSpacing\>0 or the Panel.ChildSizing.LeftRightSpacing\>0 the buttons would be moved so that the defined space is used.
8383
8484Only child controls with the following values are moved:
8585
8686- Anchors=\[akLeft,akRight\]
8787- AnchorSide\[akLeft\].Control=nil
8888- AnchorSide\[akTop\].Control=nil
8989- Align=alNone
9090
9191The moving of the child controls depend on the propert [[Autosize / Layout|ChildSizing.Layout]]. The layout is applied in the method TWinControl.AlignControls which can be overridden completely or in parts. For example TToolBar overrides ControlsAligned to position all controls with Align=alCustom and defines a flow layout where controls that do not fit are put into subsequent lines.
9292
9393Controls can disable the moving of child controls by setting the ControlStyle flag csAutoSizeKeepChildLeft and csAutoSizeKeepChildTop (since 0.9.29).
9494
9595### AutoSize and Forms
9696
9797Forms without Parent are controlled by the window manager and can therefore not be placed or resized freely. Setting the size is only a recommendation and might be ignored by the window manager. For example you might set the Width of a Form to 1000 and the widget set might answer with a resize to 800. If you set the Width in OnResize you might create an endless loop. That's why the [[LCL]] TForm disables AutoSize when the widget set resizes the form.
9898
9999That means AutoSize for forms stops when the user resizes the form or if the window manager does not like the bounds. For example some Linux window managers have features like magnetic sides which resizes windows in relationship to other windows.
100100
101101#### Force the auto sizing of a form
102102
103103You can start/execute a new AutoSize by doing:
104104
105105``` pascal
106106Form1.AutoSize := False;
107107Form1.AutoSize := True;
108108```
109109
110110#### Compute the size of an autosized form in advance
111111
112112When placing an autosized form you might need the size of the form before showing it. Autosizing requires the handle. You can compute the size of a form before showing it with:
113113
114114``` pascal
115115Form1.HandleNeeded;
116116Form1.GetPreferredSize(PreferredWidth,PreferredHeight);
117117```
118118
119119**Note**: The window manager and form events might change the size. The preferred size does not include the form's window borders. This is a planned feature.
120120
121121## Anchor Sides
122122
123123Controls have four sides: akLeft, akTop, akRight and akBottom. Each side can be anchored to the parent or the side of another sibling (a control with the same parent). Anchoring means **keeping the distance**. The default is Anchors=\[akLeft,akTop\]. Vertical anchors are independent of horizontal anchors. Some properties like Align and Parent.AutoSize have higher precedence and can change the behaviour.
124124
125125### Anchored to Parent or Nil
126126
127127Anchoring to Nil (the default) has almost the same effect as anchoring to the Parent. Both try to keep the distance to a side of the Parent's client area. Anchoring to Nil uses the distance of the last SetBounds call, while Anchoring to Parent uses the BorderSpacing value.
128128
129129There are four combinations of akLeft,akRight (akTop,akBottom):
130130
131131- **akLeft, no akRight**: the control's **Left** is fixed and not changed by the [[LCL]]. The right side is not anchored, so it follows the left side. That means the **Width** is kept too.
132132- **akLeft and akRight**: the control's **Left** is fixed and not changed by the [[LCL]]. The right side is anchored to the Parent's right side. That means if the Parent is enlarged by 100pixel then the control's **Width** is enlarged by 100pixel too.
133133- **akRight, no akLeft**: the control's left side is not anchored, so it will follow its right side. The right side is anchored. That means if the parent is enlarged by 100pixel then the control is moved to the right by 100pixel.
134134- **no akLeft and no akRight**: neither sides are anchored. The position of the center of the control scales with the parent. For example if the control is in the middle of the parent and the parent is enlarged by 100 pixel, then the control is moved 50pixel to the right.
135135
136136#### Changing the size of the parent of an anchored control
137137
138138When changing the size of a parent all anchored child controls are moved and/or resized immediately unless AutoSizing is disabled. For example AutoSizing is disabled during loading of a form and during creation of a form.
139139
140140A GroupBox with a Button: ![](/wiki-images/6/60/Anchors1.png)
141141
142142When the GroupBox is enlarged the distance of the anchored side is kept:
143143
144144With akLeft, no akRight: ![](/wiki-images/0/05/Anchors_akLeft.png)
145145
146146With akLeft and akRight: ![](/wiki-images/6/66/Anchors_akLeft_akRight.png)
147147
148148With akRight, no akLeft: ![](/wiki-images/d/d3/Anchors_akRight_no_akLeft.png)
149149
150150Three buttons in a groupbox: ![](/wiki-images/4/48/Anchors_no_akLeft_no_akRight_small.png)
151151
152152With no akLeft and no akRight their centers are scaled: ![](/wiki-images/7/7b/Anchors_no_akLeft_no_akRight_big.png)
153153
154154**Notes:**
155155
156156- Loading a form is like one big SetBounds. During loading properties are set using the values of the lfm. Keep in mind that there can be multiple lfm files because of ancestors and frames. After loading the [[LCL]] enables autosizing. Anchored controls use the bounds at the end of loading. Any step in between is ignored.
157157- For custom controls it is often better to set AnchorSides instead of only Anchors.
158158
159159#### Changing the size of an anchored control
160160
161161When changing the Width of an anchored control, for example via the Object Inspector or via code *Button1.Width:=3*, you can see the difference between anchoring to *Parent* and anchoring to *Nil*. Anchoring to Parent will resize and move the Button1, while anchoring to Nil will only resize. For example:
162162
163163##### Anchored to nil
164164
165165![](/wiki-images/b/bd/Anchored_right_nil_resize1.png) A Button1 anchored \[akTop,akRight\], AnchorSide\[akRight\].Control=nil
166166
167167![](/wiki-images/a/a0/Anchored_right_nil_resize2.png) Setting the Width to a smaller value will shrink the button, keeping the button's *Left*, increasing the distance of the right side.
168168
169169![](/wiki-images/1/16/Anchored_right_nil_resize3.png) When the Groupbox is resized the button will keep the new distance.
170170
171171**Explanation:** setting the **Width** is equivalent to calling SetBounds(**Left**,Top,**NewWidth**,Height). That's why the *Left* is kept. This is Delphi compatible.
172172
173173##### Anchored to Parent
174174
175175![](/wiki-images/4/45/Anchored_right_parent_resize1.png) A Button1 anchored \[akTop,akRight\], AnchorSide\[akRight\].Control=Button1.Parent
176176
177177![](/wiki-images/0/04/Anchored_right_parent_resize2.png) Setting the Width to a smaller value will shrink the button, keeping the right distance, changing the button's *Left*.
178178
179179#### Anchored to sibling
180180
181181You can anchor to neighbor controls. The following example shows:
182182
183183- you can anchor a label's left to the left of a button
184184- anchor a label's top to the bottom of a button
185185- anchor the center of a label to the center of a button
186186
187187![](/wiki-images/e/ee/Anchorsides_example1.png)
188188
189189![](/wiki-images/b/ba/Anchorside_example2.png)
190190
191191For more details and how to setup anchors see: [[Anchor Sides]].
192192
193193![](/wiki-images/6/6d/Anchoreditor.png)
194194
195195### BorderSpacing
196196
197197The BorderSpacing properties controls the minimum amount of space around a control. The properties are:
198198
199199- **Around**: this amount in pixel is added to Left, Top, Right, Bottom.
200200- **Left**: space in pixel on the left side of the control
201201- **Top**: space in pixel above the control
202202- **Right**: space in pixel on the right side of the control
203203- **Bottom**: space in pixel below the control
204204- **InnerBorder**: this amount in pixel is added twice to the preferred width and height. Some controls override the calculation and ignore this property. An example where it works is TButton. With InnerBorder you can make a button bigger than needed.
205205- **CellAlignHorizontal**: This is used in table layouts like ChildSizing.Layout=cclLeftToRightThenTopToBottom. If the control is smaller than the table cell, this property defines where to align the control: to the left ccaLeftTop, to the right ccaRightBottom or in the middle ccaCenter.
206206- **CellAlignVertical**: same as CellAlignHorizontal but for vertical alignment.
207207
208208#### BorderSpacing rules
209209
210210- Around is added to Left,Top,Right,Bottom borderspacing
211211- The space can be even bigger, if the controls have constraints that don't allow to expand.
212212
213213##### Anchoring to the opposite side
214214
215215For example right side of A to left side of B.
216216
217217Both borderspacings of A and B are used.
218218
219219- The horizontal space between two controls (LeftControl, RightControl on Parent) is the maximum of
220220 - LeftControl.BorderSpacing.Right + LeftControl.BorderSpacing.Around
221221 - RightControl.BorderSpacing.Left + RightControl.BorderSpacing.Around
222222 - Parent.ChildSizing.HorizontalSpacing
223223- The vertical space works analog: between two controls (TopControl, BottomControl on Parent) is the maximum of
224224 - TopControl.BorderSpacing.Bottom + TopControl.BorderSpacing.Around
225225 - BottomControl.BorderSpacing.Top + BottomControl.BorderSpacing.Around
226226 - Parent.ChildSizing.VerticalSpacing
227227
228228For instance if LeftControl.BorderSpacing.Right = 3 and LeftControl.BorderSpacing.Around = 4, then there is at least 7 pixel space between the two controls. If RightControl.BorderSpacing.Left = 4 and RightControl.BorderSpacing.Around = 4 then the space will be at least 8. If Parent.ChildSizing.HorizontalSpacing = 10 then the space will be at least 10.
229229
230230##### Anchoring to the same side
231231
232232For example right side of A to right side of B.
233233
234234- Only borderspacings of A is used.
235235- Parent.ChildSizing.Horizontal/VerticalSpacing are not used.
236236
237237##### Anchoring to center
238238
239239For example center of A to center of B.
240240
241241No borderspacing is used and no Parent.ChildSizing.Horizontal/VerticalSpacing is used.
242242
243243##### Example
244244
245245A common example is anchoring a Label to an Edit.
246246
247247![](/wiki-images/1/1e/BorderSpacing_Anchors.png)
248248
249249The Label center is vertically anchored to the Edit. The Edit's left side is anchored to the right side of the Label. Both have BorderSpacing.Around=6. This results in 6 pixel space between the Label and the Edit and 6 pixel space left of the Label and 6 pixel space right of the Edit. There is 6 pixel space above and below the Edit as well.
250250
251251- The left space between a control and its parent (Label1 on GroupBox1) is the maximum of
252252 - Label1.BorderSpacing.Left + Label1.BorderSpacing.Around
253253 - GroupBox1.ChildSizing.LeftTopSpacing
254254
255255- The right space between a control and its parent (Edit1 on GroupBox1) is the maximum of
256256 - Edit1.BorderSpacing.Right + Edit1.BorderSpacing.Around
257257 - GroupBox1.ChildSizing.RightBottomSpacing
258258
259259- When a control's center is anchored to another control, for example the above Label is centered vertically to the Edit1, then all spacings are ignored.
260260
261261- When a control's left side is anchored to the left side of another control (i.e. they are aligned with the left side), then the distance between both left's side is the
262262 - Control1.BorderSpacing.Left + Control1.BorderSpacing.Around
263263
264264Here is a more complex example:
265265
266266![](/wiki-images/9/9c/Borderspacing_anchors2.png)
267267
268268The important parts of the lfm code:
269269
270-``` pascal
270+``` lfm
271271 object GroupBox1: TGroupBox
272272 AutoSize = True
273273 Caption = 'GroupBox1'
274274 TabOrder = 0
275275 object Label1: TLabel
276276 AnchorSideLeft.Control = GroupBox1
277277 AnchorSideTop.Control = Edit1
278278 AnchorSideTop.Side = asrCenter
279279 BorderSpacing.Around = 6
280280 Caption = 'Label1'
281281 end
282282 object Edit1: TEdit
283283 AnchorSideLeft.Control = Label1
284284 AnchorSideLeft.Side = asrBottom
285285 AnchorSideTop.Control = GroupBox1
286286 AnchorSideRight.Control = GroupBox1
287287 AnchorSideRight.Side = asrBottom
288288 Anchors = [akTop, akLeft, akRight]
289289 BorderSpacing.Around = 6
290290 TabOrder = 0
291291 Text = 'Edit1'
292292 end
293293 object Label2: TLabel
294294 AnchorSideLeft.Control = GroupBox1
295295 AnchorSideTop.Control = Edit1
296296 AnchorSideTop.Side = asrBottom
297297 BorderSpacing.Around = 6
298298 Caption = 'Label2'
299299 end
300300 object ComboBox1: TComboBox
301301 AnchorSideLeft.Control = Label2
302302 AnchorSideTop.Control = Label2
303303 AnchorSideTop.Side = asrBottom
304304 AnchorSideRight.Control = GroupBox1
305305 AnchorSideRight.Side = asrBottom
306306 Anchors = [akTop, akLeft, akRight]
307307 BorderSpacing.Right = 6
308308 TabOrder = 1
309309 Text = 'ComboBox1'
310310 end
311311 object CheckBox1: TCheckBox
312312 AnchorSideLeft.Control = GroupBox1
313313 AnchorSideTop.Control = ComboBox1
314314 AnchorSideTop.Side = asrBottom
315315 BorderSpacing.Around = 6
316316 Caption = 'CheckBox1'
317317 TabOrder = 2
318318 end
319319 object Label3: TLabel
320320 AnchorSideLeft.Control = GroupBox1
321321 AnchorSideTop.Control = Edit2
322322 AnchorSideTop.Side = asrCenter
323323 BorderSpacing.Around = 6
324324 Caption = 'Label3'
325325 end
326326 object Edit2: TEdit
327327 AnchorSideLeft.Control = Label3
328328 AnchorSideLeft.Side = asrBottom
329329 AnchorSideTop.Control = CheckBox1
330330 AnchorSideTop.Side = asrBottom
331331 BorderSpacing.Around = 6
332332 TabOrder = 3
333333 Text = 'Edit2'
334334 end
335335 end
336336```
337337
338338#### BorderSpacing and Align
339339
340340BorderSpacing works with Align. In the example below there is a Memo1 with Align=alTop and a Memo2 with Align=alCLient.
341341
342342- Normally the two Memos would fill the whole GroupBox.
343343- But the Memo1 has BorderSpacing.Around=10, so there 10 pixel space around Memo1.
344344- The Memo2 has BorderSpacing.Top=20. The space between Memo1 and Memo2 is the maximum, which is the 20 from Memo2.
345345- Memo2 has also BorderSpacing.Right=50 so there is 50 Pixel space right of Memo2.
346346- The GroupBox can define default space for all its child controls via ChildSizing.LeftRightSpacing/VerticalSpacing/HorizontalSpacing. In this example it does not (all 0).
347347
348348![](/wiki-images/1/13/Borderspacing_align1.png)
349349
350350## Align
351351
352352The Align property works pretty much like in Delphi and can be used to quickly fill an area. For example to let a TListBox fill the entire area of its Parent, set ListBox1.Align=alClient. The align values alTop, alBottom, alLeft and alRight will place controls without overlapping if possible. That means all controls with Align in alLeft,alTop,alBottom,alRight will not overlap if there is enough space.
353353
354354The algorithm works as follows:
355355
356356- First all controls with alTop are put to the top of the client area. If there are several controls with alTop the last added/moved will be put topmost. The algorithm will try to avoid overlapping and keep the height of the control, while expanding the width to maximum. AnchorSides of the left, top and right sides are ignored. The bottom AnchorSide works normal. Borderspacing and Parent.ChildSizing spaces are considered, so you can define space around each aligned control.
357357- Then all controls with alBottom are put to the bottom of the client area. Otherwise it works analog to alTop.
358358- Then all controls with alLeft are put to the left of the client area between the alTop and alBottom controls. Otherwise it works analog to alTop.
359359- Then all controls with alRight are put to the right of the client area between the alTop and alBottom controls. Otherwise it works analog to alTop.
360360- If there is a control with alClient it will fill the remaining client.
361361- If there is more than one control with alClient they will be placed at the same position, overlapping each other. Use the Visibility property to define which one is shown.
362362
363363![](/wiki-images/e/e6/Autosize_align.png)
364364
365365### Align and BorderSpacing
366366
367367The space of BorderSpacing and of the parent's ChildSizing is applied to aligned controls. The memo below has Align=alClient.
368368
369369![](/wiki-images/b/b4/Autosize_align_borderspacing.png)
370370
371371### Align and Anchoring
372372
373373The free side of an aligned control (e.g. the right side of a **Align=alLeft**) follows the anchoring rules. If the anchor is not set, then the control will keep its **Width**. If the anchor is set, then the **Width** will change.
374374
375375### Align and AutoSize
376376
377377A control aligned with alLeft or alRight expands vertically, and will use its designed width. If *AutoSize* is set to *true*, then the *Width* will be the preferred width. The button below has *Align=alRight* and *AutoSize=true*.
378378
379379![](/wiki-images/3/35/Autosize_align_autosize.png)
380380
381381### Align and parent AutoSize
382382
383383A control aligned with alClient fills the remaining space. A parent control with *AutoSize=true* will expand or shrink to enclose its children exactly. What happens if you combine these two properties? Say you put a button with Align=alClient into a groupbox with AutoSize=true?
384384
385385![](/wiki-images/b/b1/Autosize_nested1.png)
386386
387387The [[LCL]] uses the *preferred size* of the buttons and expands or shrinks the groupbox accordingly:
388388
389389![](/wiki-images/2/28/Autosize_nested2.png)
390390
391391### alCustom
392392
393393This Align value exists for custom AutoSize algorithms and is treated by the [[LCL]] almost like alNone. Controls with alCustom are not moved by the [[LCL]] but by your custom control. You have to override CalculatePreferredSize and DoAutoSize.
394394
395395### Order of controls with same Align
396396
397397Controls with same Align are positioned in the following order. On alLeft the control with the lowest Left wins, for alTop the lowest Top, for alRight the biggest Left+Width, for alBottom the biggest Top+Height. If two controls have the same coordinate the one last moved (calls to SetBounds or SetParent) wins. Controls can override CreateControlAlignList to change the order or override DoAlignChildControls to handle the whole Align.
398398
399399Note: Before 0.9.29 controls at the same coordinate were put in Z order. This resulted in reordering in some cases and had to be fixed. There is one incompatibility: If you added several controls with alTop or alLeft without specifying bounds the controls are put at 0,0 and therefore the last added wins, where formerly the first won.
400400
401401Note for Delphians: Contrary to the VCL the LCL can and often does delay recomputing the layout. Delphi does the layout after every property change. Setting Align=alTop calls in the VCL a special AlignControls, where the current control has a special meaning. That's why in the VCL the order of setting properties is important. The LCL layout algorithm is independent of the chronological order of setting the properties.
402402
403403For example all changes in FormCreate are delayed and the layout is computed from the final property values. On the other hand ButtonClick has no delay (aka you have to add the Disable/EnableAutoSize yourself), so inserting alTop controls may have different results.
404404
405405If you want a specific order, use Disable/EnableAutoSize and set the "Top" properties. For example TForm.OnCreate is called with AutoSize disabled, so you can use:
406406
407407``` pascal
408408procedure TForm1.FormCreate(Sender: TObject);
409409begin
410410 // making sure Panel2 is above Panel1, even though Panel1 is set first:
411411 Panel1.Align:=alTop;
412412 Panel1.Top:=1;
413413 Panel2.Align:=alTop;
414414 Panel2.Top:=0;
415415end;
416416```
417417
418418## Layout
419419
420420### Rows, columns and lines
421421
422422You can align child controls in rows and columns using the ChildSizing properties. For example:
423423
424424- ChildSizing.Layout=cclLeftToRightThenTopToBottom
425425- ChildSizing.ControlsPerLine=3
426426- AutoSize=false (the groupbox does not resize to fit the child controls)
427427
428428![](/wiki-images/1/10/Autosize_layout_lefttoright.png)
429429
430430The **Layout** property defaults to cclNone. If you set **Layout** to another value, every child, that has *normal* anchors, will be aligned. *Normal* anchors means:
431431
432432- Anchors=\[akLeft,akTop\]
433433- AnchorSide\[akLeft\].Control=nil
434434- AnchorSide\[akTop\].Control=nil
435435- Align=alNone
436436
437437The value **cclLeftToRightThenTopToBottom** will put the first child at top, left, the second to the right, and so forth. This is a **line**. The property **ControlsPerLine** defines when a new line is started. In the above example each line (row) has 3 controls. There are 12 controls, so there are 4 lines (rows) each with 3 controls (columns). If *ControlsPerLine* is 0 it means unlimited controls per line - there would be only one row with all childs.
438438
439439You can see that the rows have different sizes, each row has the size of the biggest control and that the controls are resized to column width. There is no space between the rows. The space in the image comes from the used theme, not from the [[LCL]].
440440
441441### Fixed space between rows and columns
442442
443443You can add space between with these properties:
444444
445445- ChildSizing.VerticalSpacing - Space between rows
446446- ChildSizing.HorizontalSpacing - Space between columns
447447- ChildSizing.LeftRightSpacing - Space on the left and right of *all* columns
448448- ChildSizing.TopBottomSpacing - Space above and below of *all* columns
449449
450450The above example with ChildSizing.VerticalSpacing=6, ChildSizing.HorizontalSpacing=15, ChildSizing.LeftRightSpacing=30, ChildSizing.TopBottomSpacing=10, AutoSize=true
451451
452452![](/wiki-images/a/a9/Autosize_layout_parentspacing.png)
453453
454454Additionally you can add individual space for each control with its BorderSpacing properties.
455455
456456### Enlarge
457457
458458The above example resized the GroupBox to the needed space. If your GroupBox has a fixed size or if it is not freely resizable, for instance if the GroupBox should fill the whole width of the form, then the childs should enlarge. There are several modes. The default mode *ChildSizing.EnlargeHorizontal=crsAnchorAligning* is to not enlarge anything. The space on the right side will be unused.
459459
460460- crsAnchorAligning - do not use the extra space
461461- crsScaleChilds - multiply the width/height with the same factor
462462- crsHomogeneousChildResize - add to each width/height the same amount
463463- crsHomogeneousSpaceResize - add to each space between the childs the same amount
464464
465465#### crsScaleChilds
466466
467467ChildSizing.EnlargeHorizontal=crsScaleChilds, ChildSizing.EnlargeVertical=crsScaleChilds, AutoSize=false
468468
469469![](/wiki-images/b/b0/Autosize_layout_scalechilds.png)
470470
471471For example if the ClientWidth is twice as big as needed, then every child will be twice as big.
472472
473473#### crsHomogeneousChildResize
474474
475475ChildSizing.EnlargeHorizontal=crsHomogeneousChildResize, ChildSizing.EnlargeVertical=crsHomogeneousChildResize, AutoSize=false
476476
477477![](/wiki-images/3/39/Autosize_layout_homogeneouschildresize.png)
478478
479479For example if the ClientWidth is 30 pixel bigger than needed, then every child will be 10 pixel broader.
480480
481481#### crsHomogeneousSpaceResize
482482
483483ChildSizing.EnlargeHorizontal=crsHomogeneousSpaceResize, ChildSizing.EnlargeVertical=crsHomogeneousSpaceResize, AutoSize=false
484484
485485![](/wiki-images/9/96/Autosize_layout_homogeneousspaceresize.png)
486486
487487For example if the ClientWidth is 40 pixel bigger than needed, there will be 10 pixel space on the left, right and between each child.
488488
489489### Shrink
490490
491491Shrinking works similarly to enlarging. You can set different modes if there is not enough space for controls. **ShrinkHorizontal**, **ShrinkVertical**.
492492
493493### Individual cells
494494
495495In the above examples all controls were resized the same, each filled the whole *cell*. A *cell* is the space in a specific row and column. Normally a control fills the whole cell space. This can be changed with the properties **BorderSpacing.CellAlignHorizontal** and **BorderSpacing.CellAlignVertical**.
496496
497497For example set the *BorderSpacing.CellAlignHorizontal* of the fifth button to **caCenter** you will get this:
498498
499499![](/wiki-images/d/d3/Autosize_layout_cellhorizontal_cacenter.png)
500500
501501There are four possible values for CellAlignHorizontal/CellAlignVertical:
502502
503503- caFill: the child control fills the whole width (height) of the cell
504504- caCenter: the child control uses its preferred width (height) and will be centered in the cell
505505- caLeftTop: the child control uses its preferred width (height) and will be leftaligned in the cell
506506- caRightBottom: the child control uses its preferred width (height) and will be rightaligned in the cell
507507
508508![](/wiki-images/c/c0/Autosize_layout_cellalign.png)
509509
510510## Custom layout with OnResize / OnChangeBounds
511511
512512Sometimes the [[LCL]] layout is not sufficient. The below example shows a GroupBox1 with a ListBox1 and a Memo1. The ListBox1 should fill one third of the space, the Memo1 takes the rest.
513513
514514![](/wiki-images/4/4b/Autosize_onresize.png)
515515
516516Whenever the GroupBox is resized the ListBox1.Width should be one third. To achieve this set the OnResize event of the GroupBox1 to:
517517
518518``` pascal
519519procedure TForm1.GroupBox1Resize(Sender: TObject);
520520begin
521521 ListBox1.Width := GroupBox1.ClientWidth div 3;
522522end;
523523```
524524
525525### Common mistake: Wrong OnResize event
526526
527527Do not put *all* your resizing code into the Form OnResize event. The Form OnResize event is only called when the Form was resized. The child controls (e.g. a GroupBox1) is resized later, so the GroupBox1.ClientWidth has still the old value during the FormResize event. You must use the GroupBox1.OnResize event to react to changes of GroupBox1.ClientWidth.
528528
529529### Common mistake: Width instead of ClientWidth, AdjustClientRect
530530
531531The Width is the size including the frame. The ClientWidth is the inner Width without the frame. Some controls like the TPanel paints a further frame. Then you have to use AdjustClientRect. The same example, but instead of a GroupBox1 the ListBox1 is in a Panel1:
532532
533533``` pascal
534534procedure TForm1.Panel1Resize(Sender: TObject);
535535var
536536 r: TRect;
537537begin
538538 r := Panel1.ClientRect;
539539 Panel1.AdjustClientRect(r);
540540 ListBox1.Width := (r.Right - r.Left) div 3;
541541end;
542542```
543543
544544## Custom Controls
545545
546546When you write your own control, you can override and fine tune many parts of the [[LCL]] autosizing.
547547
548548### SetBounds, ChangeBounds, DoSetBounds
549549
550550**SetBounds** is called when the properties Left, Top, Width, Height, BoundsRect is set or the user calls it directly. SetBounds updates the BaseBounds and BaseParentClientSize, which are used by anchoring to keep the distance. For example loading a Form with TMemo and the lfm contains TMemo's Left and Width, then SetBounds is called two times for the memo. When the user maximizes a window, SetBounds is called for the form, but not for the Memo, keeping the BaseBounds of the Memo. If the Memo is anchored to the right, the Width of the Memo is changed based on the BaseBounds and BaseParentClientSize. Keep in mind that the given aLeft, aTop, aWidth, aHeight might not be valid and will be changed by the [[LCL]] before applied. Delphi calls SetBounds more often. SetBounds calls ChangeBounds with KeepBase=false.
551551
552552**ChangeBounds** is called whenever the position or size of the control is set, either via the properties or by the layouter of the [[LCL]]. SetBounds calls internally ChangeBounds with KeepBase=false, while the [[LCL]] layouter calls it with KeepBase=true. Override this for code that might change the preferred size or resizes other controls. Keep in mind that the given aLeft, aTop, aWidth, aHeight might not be valid and will be changed by the [[LCL]] before applied. You can call this function.
553553
554554**DoSetBounds** is a low level function to set the private variables FLeft, FTop, FWidth, FHeight. Do not call this function, only the [[LCL]] calls it. It also updates FClientWidth and FClientHeight accordingly. Override this to update the content layout of the control, for example scroll bars. As always: do not paint here, but call Invalidate and paint in OnPaint or override Paint.
555555
556556**DoAdjustClientRectChange** is called by the [[LCL]] and the [[LCL]] interface, when the ClientRect has changed and the Width and Height were kept.
557557
558558**WMSize** exists for Delphi/VCL compatibility. It is called by the [[LCL]] interface and on every change of bounds.
559559
560560### AdjustClientRect
561561
562562The method AdjustClientRect can be overriden by your custom controls and affects Align, ChildSizing.Layout and AnchorSides. It does not affect the meaning of Left, Top and does not affect normal anchoring (for example setting).
563563
564564When you want to draw your own frame, then the child controls should be aligned within these frames. For example TPanel draws a frame and reduces the client area by overriding the method **AdjustClientRect**:
565565
566566``` pascal
567567 TCustomPanel = class(TCustomControl)
568568 ...
569569 protected
570570 ...
571571 procedure AdjustClientRect(var aRect: TRect); override;
572572 ...
573573
574574procedure TCustomPanel.AdjustClientRect(var aRect: TRect);
575575var
576576 BevelSize: Integer;
577577begin
578578 inherited AdjustClientRect(aRect);
579579
580580 BevelSize := BorderWidth;
581581 if (BevelOuter <> bvNone) then
582582 inc(BevelSize, BevelWidth);
583583 if (BevelInner <> bvNone) then
584584 inc(BevelSize, BevelWidth);
585585
586586 InflateRect(aRect, -BevelSize, -BevelSize);
587587end;
588588```
589589
590590#### AdjustClientRect and Align
591591
592592AdjustClientRect can be used to reduce the client area used by all autosize operations. For example TPanel uses AdjustClientRect to reduce the client area by the borderwidth:
593593
594594![](/wiki-images/c/ca/Adjustclientrect_align.png)
595595
596596The Button1 in the screenshot was created with Align=alClient. ChildSizing.Layout is affected too.
597597
598598When anchoring to a parent via AnchorSides the AdjustClientRect is used too:
599599
600600``` pascal
601601 Button1.AnchorParallel(akTop,0,Button1.Parent);
602602```
603603
604604The Button1's top side is anchored to the top side of the parent's client area. If the AdjustClientRect adds 3 to the Top the Button1.Top will be 3 (3 plus 0).
605605
606606### Own AutoSize
607607
608608When *AutoSize* is set to true the control should be resized to the preferred size if possible.
609609
610610#### Preferred Size
611611
612612The new size is fetched by the [[LCL]] via **GetPreferredSize** which calls **CalculatePreferredSize**, which can be overridden. For example let's write a **TQuadrat**, which is a TShape, but its height should equal its width:
613613
614614``` pascal
615615 TQuadrat = class(TShape)
616616 protected
617617 procedure CalculatePreferredSize(var PreferredWidth,
618618 PreferredHeight: integer; WithThemeSpace: Boolean); override;
619619 end;
620620...
621621procedure TQuadrat.CalculatePreferredSize(var PreferredWidth,
622622 PreferredHeight: integer; WithThemeSpace: Boolean);
623623begin
624624 PreferredHeight:=Width;
625625end;
626626```
627627
628628The method CalculatePreferredSize gets two var parameters: *PreferredWidth* and *PreferredHeight*. They default to 0 which means: there is no preferred size, so the [[LCL]] will not change the size. The above function sets *PreferredHeight* to the current *Width*. The boolean parameter *WithThemeSpace* is deprecated and always false.
629629
630630**Important**: CalculatePreferredSize must not change the bounds or any other value of the control that can trigger an autosize. Doing so will create a loop.
631631
632632Computing the *PreferredWidth/Height* can be expensive. Therefore the [[LCL]] caches the result until *InvalidatePreferredSize* is called for the control. In our example the *PreferredHeight* depends on the *Width*, so we must invalidate when the *Width* changes:
633633
634634``` pascal
635635 TQuadrat = class(TShape)
636636 protected
637637 ...
638638 procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;
639639 end;
640640...
641641procedure TQuadrat.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);
642642begin
643643 inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);
644644 InvalidatePreferredSize;
645645 // Note: The AdjustSize can be omitted here, because the [[LCL]] does that after calling DoSetBounds.
646646end;
647647```
648648
649649The [[LCL]] will automatically trigger the autosizing when the bounds have changed, so the example is complete.
650650
651651The default TWinControl implementation of CalculatePreferredSize queries the widget set, which might return a preferred width and/or height. Each control can override the CalculatePreferredSize method. For example TImage overrides it and returns the size of the Picture. If no preferred width (height) is available the returned value is 0 and the [[LCL]] will keep the current Width (Height). If 0 is a valid size for your control, you must set the ControlStyle flag csAutoSize0x0 (ControlStyle:=ControlStyle+\[csAutoSize0x0\];). An example is the [[LCL]] control TPanel.
652652
653653#### AdjustSize
654654
655655When the preferred size depends on a new property, then every time the property changes the auto sizing must be triggered. For example:
656656
657657``` pascal
658658procedure TQuadrat.SetSubTitle(const AValue: string);
659659begin
660660 if FSubTitle = AValue then exit;
661661 FSubTitle := AValue;
662662 InvalidatePreferredSize;
663663 AdjustSize;
664664end;
665665```
666666
667667## Reduce overhead with DisableAutoSizing, EnableAutoSizing
668668
669669Since Lazarus 0.9.29 there is a new autosizing algorithm, that reduces the overhead and allows deep nested dependencies. Up to 0.9.28 DisableAlign/EnableAlign and Disable/EnableAutoSizing worked only for one control and its direct child controls.
670670
671671Each time you change a property the [[LCL]] triggers a recomputation of the layout:
672672
673673``` pascal
674674Label1.Caption := 'A'; // first recompute
675675Label2.Caption := 'B'; // second recompute
676676```
677677
678678The recomputations will trigger events and send messages. To reduce the overhead you can suspend the autosizing:
679679
680680``` pascal
681681DisableAutoSizing;
682682try
683683 Label1.Caption := 'A'; // no recompute
684684 Label2.Caption := 'B'; // no recompute
685685finally
686686 EnableAutoSizing; // one recompute
687687end;
688688```
689689
690690You must balance calls to Disable/EnableAutoSizing. Autosizing starts only when EnableAutoSizing is called after a corresponding (earlier) DisableAutoSizing.
691691
692692Since 0.9.29 the Disable/EnableAutoSizing works for the entire form. This means every call to DisableAutoSizing suspends autosizing for **all** controls on that form. If you write your own control you can now use the following:
693693
694694``` pascal
695695procedure TMyRadioGroup.DoSomething;
696696begin
697697 DisableAutoSizing; // disables not only TMyRadioGroup, but the whole form's autosizing
698698 try
699699 // delete items ...
700700 // add, reorder items ...
701701 // change item captions ...
702702 finally
703703 EnableAutoSizing; // recompute
704704 end;
705705end;
706706```
707707
708708Which basically means: you do not have to care. Just call Disable/EnableAutoSizing.
709709
710710Note: this is wrong:
711711
712712``` pascal
713713Button1.DisableAutoSizing;
714714Label1.EnableAutoSizing; // wrong: every control has its own autosizing reference counter
715715```
716716
717717### DisableAutoSizing and Form bounds
718718
719719DisableAutoSizing has another useful effect under asynchronous Window Managers such as you find in Linux systems. Each time a form is resized or moved the bounds are sent to the widget set (DoSendBoundsToInterface). Even if the form is not shown the Handle is resized. The window manager often treats these bounds only as a proposal. The window manager has its own logic and often only the first bounds sent are used. The second, third or further moves may be ignored. With DisableAutoSizing you can make sure that only the final bounds are sent to the widget set making the form bounds more reliable.
720720
721721## Example: a button panel
722722
723723This example combines several of the [[LCL]] layout mechanisms to create a panel with three buttons: a *Help* button to the left and *Ok* and *Cancel* buttons to the right. We want the panel to be at the bottom of the form filling the entire width. The buttons and the panel are autosized to fit all fonts and themes.
724724
725725Step 1: Create the panel and set its Align property to alBottom. Add three TBitBtns.
726726
727727![](/wiki-images/7/7e/Autosize_example_buttonpanel1.png)
728728
729729Set the *Kind* properties of the BitBtns to show the glyphs. You might need to set GlyphShowMode to gsmAlways to see them on your platform. Set the BitBtn's *AutoSize* property to *True*, which will shrink/enlarge the buttons to fit perfectly around the glyphs and text. Depending on your theme and platform you might notice that the buttons have different heights.
730730
731731![](/wiki-images/4/49/Autosize_example_buttonpanel2.png)
732732
733733Set the *Align* property of the help button to *alLeft* and set the other two buttons' *Align* property to *alRight*. This will enlarge the buttons vertically and move them to the far left/right. alLeft/alRight has no effect on the width, so the buttons use their preferred width.
734734
735735![](/wiki-images/b/b6/Autosize_example_buttonpanel3.png)
736736
737737The Panel height is still fixed. Now set the panel *AutoSize* property to *True*. The panel now shrinks vertically to fit the tallest button.
738738
739739![](/wiki-images/3/36/Autosize_example_buttonpanel4.png)
740740
741741The *Ok* button has a short caption, so the button is very small. Set the button's *Constraints.MinWidth* to *75*. The button will now enlarge somewhat.
742742
743743![](/wiki-images/3/32/Autosize_example_buttonpanel5.png)
744744
745745Now add some space around the buttons. Set the panel's *ChildSizing.LeftTopSpacing/RightBottomSpacing/HorizontalSpacing* to *6*.
746746
747747![](/wiki-images/4/46/Autosize_example_buttonpanel6.png)
748748
749749Finally clear the *Caption* of the panel and set its *BevelOuter* to *bvNone*.
750750
751751![](/wiki-images/4/4e/Autosize_example_buttonpanel7.png)
752752
753753## Scrolling
754754
755755Some [[LCL]] controls like *TScrollBox*, *TForm*, and *TFrame* show scrollbars if the child controls are too big to fit the scrollbox, form or frame. They inherit this behaviour from their ancestor **TScrollingWinControl**.
756756
757757A scrolling control's **logical client area** can be bigger than the **visible client area**. The *visible client area* is **ClientRect**. It always starts at 0,0 and its width and height is the inner area. For example in a TGroupBox it is the size of the area inside the frame. So the following is always true:
758758
759759``` pascal
760760ClientWidth <= Width
761761ClientHeight <= Height
762762```
763763
764764The **logical client area** is defined by the method **GetLogicalClientRect**. By default it is the same as *ClientRect*. When a child control is anchored to the right side, it uses the *logical client area*. *TScrollingWinControl* overrides this method and returns the *Range* of the scrollbars if they are bigger than the *ClientRect*. The **Range** can be set manually or automatically with *AutoScroll=true*. An example for *AutoScroll=true*:
765765
766766![](/wiki-images/d/db/Autoscroll_all_fit1.png)
767767
768768The upper button has a fixed Width of 200. The lower button is anchored to the right side of the panel. Therefore the child controls have a preferred width of 200. Because the panel is bigger than 200 the logical client area is bigger too and the lower button expands.
769769
770770Now the panel is shrunk, so that the ClientWidth becomes lower than 200:
771771
772772![](/wiki-images/2/27/Autoscroll_not_fit1.png)
773773
774774The preferred width is still 200, so the logical client area is now 200 and bigger than the visible client area. The lower button has now a Width of 200 and the panel shows a horizontal scrollbar.
775775
776776### Scroll Position
777777
778778Changing the Position of a scrollbar does not change the Left or Top of any child control, nor does it change the logical client area, nor does it affect autosizing. The child controls are only virtually moved.
779779
780780### Scrolling and AutoSize
781781
782782When AutoSize=true the [[LCL]] will expand the control to accommodate all its child controls, and no scrollbars are needed. If the control cannot be expanded, then (only) is the secondary action of AutoSize executed: moving the child controls.
783783
784784## Docking
785785
786786Docking uses the described methods and properties of this page, see [[Docking]].
787787
788788## Splitter
789789
790790See [[TSplitter]].
791791
792792## TLabel.WordWrap
793793
794794**TLabel.WordWrap** changes the behavior of the preferred size of the label. **WordWrap=true** requires that the Width of the label is fixed, for example by anchoring the left and right size of the label. The preferred height of the label is then computed by breaking the Caption into multiple lines.
795795
796796## DPI auto-adjustment and absolute layout auto-adjustment
797797
798798Historically the [[LCL]] has been utilized mostly to design absolute layouts, despite the huge amount of options which Lazarus offers for flexible layouts, like Align, Anchors, etc, as described on the rest of this article. On top of that, it has also historically ignored the DPI of the target and instead utilized values in pixels to measure the left, top, width and height properties of controls. For desktop platforms and Windows CE this has worked reasonably well, but with the advent of [[LCL]] support for Android this could no longer be ignored. Starting in Lazarus 0.9.31 the [[LCL]] can reinterprete the [[LCL]] absolute layout in pixels as a flexible grid. There are multiple modes to choose from and they will allow to reinterprete the pixel values as either really absolute, or as in being adjusted for the DPI or as in being considered simply a fraction of the form size.
799799
800800It is important to note that these new rules affect only controls which are positioned without Align and with the most standard Anchors only.
801801
802802In the case where the values will be adjusted for DPI, there is a new property: TCustomForm.DesignTimeDPI which should store the DPI value of the system where the form was designed. The positioning values will be expanded when the target DPI is larger than the design time DPI or reduced otherwise. The common value for desktop DPIs is 96 and is the default value given.
803803
804804property DesignTimeDPI: Integer read FDesignTimeDPI write FDesignTimeDPI;
805805
806806The way in which the layout is adjusted can be controlled with the property TApplication.LayoutAdjustmentPolicy
807807
808808``` pascal
809809 TLayoutAdjustmentPolicy = (
810810 lapDefault, // widget set dependent
811811 lapFixedLayout, // A fixed absolute layout in all platforms
812812 lapAutoAdjustWithoutHorizontalScrolling, // Smartphone platforms use this one,
813813 // the x axis is stretched to fill the screen and
814814 // the y is scaled to fit the DPI
815815 lapAutoAdjustForDPI // For desktops using High DPI, scale x and y to fit the DPI
816816 );
817817```
818818
819819And the following new methods in TControl allow to force a layout-autoadjustment in a particular control and all its children or to control how particular descendants of TControl react to this:
820820
821821``` pascal
822822TControl = class
823823public
824824...
825825 procedure AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;
826826 const AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth: Integer); virtual;
827827 function ShouldAutoAdjustLeftAndTop: Boolean; virtual;
828828 function ShouldAutoAdjustWidthAndHeight: Boolean; virtual;
829829```
830830
831831LCL-CustomDrawn-Android will call AutoAdjustLayout when the screen rotates, for example.
832832
833833## More details
834834
835835Many controls override *TControl.DoAutoSize* to perform the actual auto-sizing.
836836
837837IMPORTANT: Many Delphi controls override this method and many call this method directly after setting some properties.
838838
839839During handle creation not all interfaces can create complete Device Contexts which are needed to calculate things like text size.
840840
841841That's why you should always call *AdjustSize* instead of `DoAutoSize`.
842842
843843`TControl.AdjustSize` calls `DoAutoSize` in a smart fashion.
844844
845845During loading and handle creation the calls are delayed.
846846
847847This method initially does the same as *TWinControl.DoAutoSize*. But since `DoAutoSize` is commonly overriden by descendant components, it is not useful to perform all tests, which can result in too much overhead. To reduce this the [[LCL]] calls `AdjustSize` instead.
848848
849849When setting AutoSize = true the [[LCL]] autosizes the control in width and height. This is one of the most complex parts of the [[LCL]], because the result depends on nearly a hundred properties. Let's start simple:
850850
851851The [[LCL]] will only autosize the *Width* (Height) if it is free to resize. In other words - the width is not autosized if:
852852
853853- the left and right side is anchored. You can anchor the sides with the **Anchors** property or by setting the *Align* property to alTop, alBottom or alClient.
854854- the *Width* is bound by the *Constraints* properties. The Contraints can also be overriden by the widget set. For example the winapi does not allow resizing the height of a combobox. And the gtk widget set does not allow resizing the width of a vertical scrollbar.
855855
856856Same for *Height*.
857857
858858The new size is calculated by the protected method **TControl.CalculatePreferredSize**. This method asks the widget set for an appropriate Width and Height. For example a TButton has preferred Width and Height. A TComboBox has only a preferred Height. The preferred Width is returned as 0 and so the [[LCL]] does not autosize the Width - it keeps the width unaltered. Finally a TMemo has no preferred Width or Height. Therefore AutoSize has no effect on a TMemo.
859859
860860Some controls override this method. For example the TGraphicControl descendants like TLabel have no window handle and so cannot query the widget set. They must calculate their preferred Width and Height themselves.
861861
862862The widget sets must override the GetPreferredSize method for each widget class that has a preferred size (Width or Height or both).
863863
864864### Parent.AutoSize
865865
866866The above described the simple explanation. The real algorithm provides far more possibilities and is therefore far more complex.
867867
868868### Properties / Methods
869869
870870- Left
871871- Top
872872
873873If Parent\<\>nil then Left, Top are the pixel distance to the top, left pixel of the parent's client area (not scrolled). Remember the client area is always *without* the frame and scrollbars of the parent. For Delphi users: Some VCL controls like TGroupbox define the client area as the whole control including the frame and some not - the [[LCL]] is more consistent, and therefore Delphi incompatible. Left and Top can be negative or bigger than the client area. Some widget sets define a minimum/maximum somewhere around 10.000 or more.
874874
875875When the client area is scrolled the Left and Top are kept unchanged.
876876
877877During resizing/moving Left and Top are not always in sync with the coordinates of the Handle object.
878878
879879If Parent=nil then Left, Top depend on the widget set and the window manager. Till Lazarus 0.9.25 this is typically the screen coordinate of the left,top of the client area of the form. This is Delphi incompatible. It is planned to change this to the Left, Top of the window.
880880
881881Hint: Each time you change Left and Top the [[LCL]] moves instantly and recomputes the whole layout. If you want to change Left *and* Top use instead:
882882
883883``` pascal
884884with Button1 do
885885 SetBounds(NewLeft, NewTop, Width, Height);
886886```
887887
888888- Width
889889- Height
890890
891891The Size in pixels must not be negative, and most widget sets do not allow Width=0 and/or Height=0. Some controls on some platforms define a bigger minimum constraint in Constraints.MinInterfaceWidth/Height. Instead of sizing a control to Width=0 and/or Height=0, set Visible=false or Parent=nil. During resizing/moving Width and Height are not always in sync with the size of the Handle object.
892892
893893- BoundsRect
894894
895895Same as Bounds(Left, Top, Width, Height).
896896
897897Common newbie mistake:
898898
899899``` pascal
900900BoundsRect.Left := 3; // WRONG: common newbie mistake
901901```
902902
903903This has no effect, because reading BoundsRect is a function. It creates a temporary TRect on the stack. The above is the same as
904904
905905``` pascal
906906var
907907 r: TRect;
908908begin
909909 r := BoundsRect; // fetch the bounds
910910 r.Left := 3; // change a value on the stack
911911end; // no change
912912```
913913
914914- ClientRect
915915
916916Left and Top are always 0,0. Width and Height are the visible size in pixels of the client area. Remember the client area is without the frame and without scrollbars. In a scrollable client area the logical client area can be bigger than the visible.
917917
918918- ClientOrigin
919919
920920Returns the screen coordinate of the topleft coordinate 0,0 of the client area. Note that this value is the position as stored in the interface and is not always in sync with the [[LCL]]. When a control is moved, the [[LCL]] sets the bounds to the desired position and sends a move message to the interface. It is up to the interface to handle moves instantly or queued.
921921
922922- LCLIntf.GetClientBounds
923923
924924Returns the client bounds of a control. Like ClientRect, but Left and Top are the pixel distances to the control's left, top. For example on a TGroupBox the Left, Top are the width and height of the left and top frame border. Scrolling has no effect on GetClientBounds.
925925
926926- LCLIntf.GetWindowRect
927927
928928After the call, ARect will be the control area in screen coordinates. That means, Left and Top will be the screen coordinate of the TopLeft pixel of the Handle object and Right and Bottom will be the screen coordinate of the BottomRight pixel.
929929
930930- FBaseBoundsLock: integer
931931
932932Increased/Decreased by LockBaseBounds/UnlockBaseBounds. Used to keep FBaseBounds during SetBounds calls.
933933
934934- FBaseParentClientSize: TPoint
935935
936936The Parent.ClientRect size valid for the FBaseBounds. FBaseBounds and FBaseParentClientSize are used to calculate the distance for akRight (akBottom). When the parent is resized, the [[LCL]] knows what distance to keep.
937937
938938- FBoundsRectForNewParent: TRect
939939
940940When changing the Parent of a control the Handle is recreated and many things can happen. Especially for docking forms the process is too unreliable. Therefore the BoundsRect is saved. The VCL uses a similar mechanism.
941941
942942- fLastAlignedBounds: TRect
943943
944944See TControl.SetAlignedBounds for an explanation. In short: It stops some circles between interface and [[LCL]] autosizing.
945945
946946- FLastChangebounds: TRect
947947
948948Used to stop calling ChangeBounds with the same coordinates. This happens very often.
949949
950950- FLastDoChangeBounds: TRect
951951
952952Used to avoid calling OnChangeBounds with the same coordinates. This reduces user defined autosizing.
953953
954954- FLastResizeClientHeight: integer
955955- FLastResizeClientWidth: integer
956956- FLastResizeHeight: integer
957957- FLastResizeWidth: integer
958958
959959Used to avoid calling OnResize with the same coordinates. This reduces user defined autosizing.
960960
961961- FLoadedClientSize: TPoint
962962
963963During loading many things are delayed and many things are set and worse: in the wrong order. That's why SetClientWidth/SetClientHeight calls are stored and set at end of loading again. This way the [[LCL]] can restore the distances (e.g. akRight) used during designing.
964964
965965- FReadBounds: TRect
966966
967967Same as FLoadedClientSize, but for SetLeft, SetTop, SetWidth, SetHeight.
968968
969969- procedure SetBoundsRectForNewParent(const AValue: TRect);
970970
971971Used to set FBoundsRectForNewParent. See above.
972972
973973- procedure SetAlignedBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;
974974
975975As SetBounds but without changing the default sizes.
976976
977977- procedure SetInitialBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;
978978
979979A smart version of SetBounds, reducing overhead during creation and loading.
980980
981981- procedure UpdateBaseBounds(StoreBounds, StoreParentClientSize, UseLoadedValues: boolean); virtual;
982982
983983Commit current bounds to base bounds.
984984
985985- procedure SetClientHeight(Value: Integer);
986986- procedure SetClientSize(Value: TPoint);
987987- procedure SetClientWidth(Value: Integer);
988988
989989Exists for Delphi compatibility too. Resizes the control, to get the wanted ClientRect size.
990990
991991- procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;
992992
993993This is the internal SetBounds. Applies constraints, updates base bounds, calls OnChangeBound, OnResize, locks bounds.
994994
995995- procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;
996996
997997This really sets the FLeft, FTop, FWidth, FHeight private variables.
998998
999999- procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;
10001000
10011001This is the standard procedure overriden by many Delphi controls. TWinControl overrides it too.
10021002
10031003- - ignores calls when bounds are locked
10041004 - lock the FBoundsRealized to avoid overhead to the interface during auto sizing.
10051005
10061006ChangeBounds is not locked this way.
10071007
10081008- Function GetClientOrigin: TPoint; virtual;
10091009
10101010Screen coordinate of Left, Top of client area.
10111011
10121012- Function GetClientRect: TRect; virtual;
10131013
10141014Size of client area. (always Left=0, Top=0)
10151015
10161016- Function GetScrolledClientRect: TRect; virtual;
10171017
10181018Visible client area in ClientRect.
10191019
10201020- function GetChildsRect(Scrolled: boolean): TRect; virtual;
10211021
10221022Returns the Client rectangle relative to the control's Left, Top. If Scrolled is true, the rectangle is moved by the current scrolling values (for an example see TScrollingWincontrol).
10231023
10241024- function GetClientScrollOffset: TPoint; virtual;
10251025
10261026Returns the scrolling offset of the client area.
10271027
10281028- function GetControlOrigin: TPoint; virtual;
10291029
10301030Returns the screen coordinate of the topleft coordinate 0,0 of the control area. (The topleft pixel of the control on the screen) Note that this value is the position as stored in the interface and is not always in sync with the [[LCL]]. When a control is moved, the [[LCL]] sets the bounds to the wanted position and sends a move message to the interface. It is up to the interface to handle moves instantly or queued.
10311031
10321032## FAQ
10331033
10341034### Why does AutoSize not work in the designer properly?
10351035
10361036In the designer controls can be dragged around and properties can be set in almost any order. To allow this and avoid possible conflicts, the AutoSizing is not updated on every change at design time.
10371037
10381038### Why does TForm.AutoSize not work when something changes?
10391039
10401040See [[Autosize / Layout|AutoSize and Forms]]
10411041
10421042### Do I need to call Application.ProcessMessages when creating lots of controls?
10431043
10441044Application.ProcessMessages is called by the [[LCL]] automatically after every message (e.g. after every event like OnClick). Calling it yourself is only needed if the changes should become visible to the user immediately. For example:
10451045
10461046``` pascal
10471047procedure TFrom.Button1Click(Sender: TObject);
10481048begin
10491049 // change width of a control
10501050 Button1.Width := Button1.Width + 10;
10511051 // apply any needed changes and repaint the button
10521052 Application.ProcessMessages;
10531053 // do a lot of things that takes a long time
10541054 ...
10551055 // after leaving the OnClick the LCL automatically processes messages
10561056end;
10571057```
10581058
10591059### When enabling Anchors at runtime the control resizes, does not use the current values. Why?
10601060
10611061akBottom means: keep a distance to the bottom side of the parent. The distance to keep is defined by the base bounds. They are set at designtime or by runtime calls of SetBounds or UpdateBaseBounds.
10621062
10631063For example: A TListBox (Anchors=\[akLeft,aTop\]) at designtime has a bottom distance of 100 pixel. And a button to enable/disable the akBottom of the TListBox. Now start the application and press the button to enable akBottom. The 100 pixel distance will be activated, because this was the last time the programmer defined the base bounds of the TListBox. All other resizes were done by the [[LCL]] and are irrelevant. The programmers base bounds rules. You can resize the form and the 100 pixel will be kept. In order to use the current bounds as base bounds use:
10641064
10651065``` pascal
10661066ListBox1.UpdateBaseBounds(true, true, false);
10671067ListBox1.Anchors := ListBox1.Anchors + [akBottom];
10681068```
10691069
10701070Setting Anchors does not automatically call UpdateBaseBounds, because this would destroy the ability to change properties independently.
10711071
10721072### Resizing stringgrid columns in form's OnResize event does not work
10731073
10741074The Form's OnResize is triggered when the Form's Width, Height, ClientWidth or ClientHeight changes. This is per se independent of TStringGrid. Of course it often happens that both the Form and the TStringGrid are resized. This means using forms OnResize will often work, but not always. A prominent example where it always fails is when the theme is changed and you have a TStringGrid in a TGroupBox in a TForm. When the theme changed the form size is kept, so no Forms OnResize is triggered. But the TGroupBox changed, so the TStringGrid should be resized.
10751075
10761076Solution: Use the TStringGrid's OnResize.
10771077
10781078### Why is TForm.Width equal to TForm.ClientWidth?
10791079
10801080Mattias' notes:
10811081
10821082"There are historical and technical reasons.
10831083
10841084For forms without parent the Clientwidth equals the Width, because the real Width including the frame was not available on Linux ten years ago (at least not reliable on various window managers). I didn't test, but I heard it is now possible with gtk2. The main problem is the autosizing, because this needs the frame sizes before the form is mapped to the screen. It might be, that this is only available after an event, which means that you have to wait for it, which means trouble for ShowModal. Changing this breaks compatibility with a lot of existing [[LCL]] code, but for this we added the LCLVersion in the lfm files.
10851085
10861086There is a new compiler define **LCLRealFormBounds** In Lazarus trunk 1.7 that enables real size for a form. To use it just compile your application with LCLRealFormBounds ON. For now, only win32 widget set is supported.
10871087
10881088For all other controls the rules is ClientWidth\<=Width. The Width is the ClientWidth plus the widget frame. The question is if the scrollbars belong to the frame. I would say yes and it was implemented that way some time ago. Apparently this has changed. See the current cursor problem on synedit."
10891089
10901090### I get an infinite loop / How to debug autosizing?
10911091
10921092Here are some notes, describing what other users did wrong and how to find out:
10931093
10941094#### Differences to Delphi
10951095
10961096For Delphi users: Please read: [[Autosize / Layout|Lazarus_vs_Delphi]]
10971097
10981098#### Overriding a [[LCL]] method and triggering a recompute
10991099
11001100You override a method and tell the [[LCL]] to compute again, even if nothing changed. Check for AdjustSize, Realign, AlignControls, InvalidatePreferredSize. For example:
11011101
11021102``` pascal
11031103procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);
11041104begin
11051105 // This will create an endless loop
11061106 InvalidatePreferredSize;
11071107
11081108 // This will create an endless loop too:
11091109 OtherComponent.Left:=10;
11101110 OtherComponent.Left:=20;
11111111
11121112 inherited SetBounds(ALeft, ATop, AWidth, AHeight);
11131113end;
11141114```
11151115
11161116Explanation: The SetBounds method is called often, even without changing anything. For example a "Left:=30;" will do so.
11171117
11181118Remedy: Check for changes:
11191119
11201120``` pascal
11211121procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);
11221122begin
11231123 if (Left <> ALeft) or (Top <> ATop) or (Width <> AWidth) or (Height <> AHeight) then
11241124 InvalidatePreferredSize;
11251125 inherited SetBounds(ALeft, ATop, AWidth, AHeight);
11261126end;
11271127```
11281128
11291129##### TWinControl.AlignControls
11301130
11311131``` pascal
11321132procedure AlignControls(aControl: TControl; var aRect: TRect); override;
11331133```
11341134
11351135AlignControls moves and sizes all child controls. The [[LCL]] implementation ignores controls with Align=alCustom.
11361136
11371137The parameter aControl: TControl is kept for VCL compatibility. The [[LCL]] always passes nil. It gives aControl precedence when applying the Align property. When you have for example two controls A,B with Align=alLeft then the one with the lower Left is put left most. If both have the same Left then creation order wins. Now imagine you want to switch both controls A,B in the designer. You drag B to the left. This results in setting B.Left to 0. Now AlignControls starts and find both A.Left=0 and B.Left=0. Normally A would win. To let B win the VCL calls AlignControls(B,r). So aControl is the last moved. Contrary to the VCL the [[LCL]] allows to combine multiple layout changes without recomputing on every step. The [[LCL]] keeps track of the last moved controls in TWinControl.fAlignControls and applies the order in TWinControl.CreateControlAlignList. The aControl parameter is always nil.
11381138
11391139See TWinControl.CreateControlAlignList.
11401140
11411141#### OnResize/OnChangeBounds conflict with [[LCL]] properties
11421142
11431143You set bounds that bite the [[LCL]] properties. For example a TLabel.AutoSize is true by default. If you set the Label1.Width in an OnResize event, the [[LCL]] will recompute, resize the Label1 and call the OnResize again. Start your application in the debugger and reproduce the bug. When it enters the loop, pause the application and see the call stack. If you see one of your events or your methods start searching there. For example:
11441144
11451145``` pascal
11461146procedure TMainForm.FormResize(Sender: TObject);
11471147begin
11481148 // if Button1 is anchored or AutoSize=true then the following might create an endless loop:
11491149 Button1.Width:=100;
11501150end;
11511151```
11521152
11531153#### [[LCL]] interface bug, custom widget
11541154
11551155Sometimes the [[LCL]] interface or your custom control has a bug and undoes the [[LCL]] bounds. Compile the [[LCL]] with -dVerboseIntfSizing. This will write the communication between [[LCL]] and the [[LCL]] interface. If a widget does not allow free resizing, it must tell the [[LCL]] via the Constraints. Search for SetInterfaceConstraints in the various [[LCL]] interfaces and TWSControlClass.ConstraintWidth/Height.
11561156
11571157#### alCustom
11581158
11591159You use alCustom. This was only half implemented in earlier Lazarus versions, but some clever programmers used it to create some special effects. Now it is implemented and your program does not work anymore. Please see here what alCustom does: [[Autosize / Layout|alCustom]].
11601160
11611161## See also
11621162
11631163- [[Docking]]
11641164- [[High DPI]]
11651165- [[Anchor Sides]]
11661166
11671167---
11681168*From the Free Pascal wiki, page "Autosize / Layout", as it stood on 2021-04-13 after a change by Trev, 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