Sign in Join
Wiki copytemporary

Autosize / Layout/zh TW: what changed

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

OldNewLine
11# 概述
22
33LCL 會自動修改 controls 的大小和位置,所以它會根據字體,主題,文字和其他內容來自動適應。如果你想讓你的程式在多個平台上執行,或者程式裡的 captions 有多個語言。那麼,你的 controls 需要正確地適應本地環境. LCL 不僅允許你用快速的第一設計模式(用滑鼠在 form 上移動 controls),也允許你之後再改動一些主要的屬性來使得 controls 自動適應後來的內容變動。
44
55- Fixed design: 當在 designer 裡放置好 control 後,它就預設地啟用. Control 的位置相對它的父組件是固定的. Control 的大小和位置(Left, Top)可以被程式員改變。你可以自由地用滑鼠移動 control 或改變其大小。
66- Aligned: 對齊的 controls 會根據設定填充父組件的頂部,底部,左邊,右邊的剩餘空間或者整個剩餘空間。
77- Anchored: 你可以在 control 的邊上(left, top, right, bottom)拋錨,錨住父組件或者另一個 control. 一旦錨住後, LCL 就會使得拋錨的那個邊和被錨住的組件的距離固定。
88- Layout: controls 可以自動地依據行列對齊(例如 TRadioGroup)
99- Custom via OnResize: 自己處理 OnResize, OnChangeBounds 事件,你就可以自己安排 controls 了。
1010- Custom controls: 自己寫 controls, 那麼你就可以按你需要覆寫幾乎所有 LCL 行為了。
1111
1212## 優先級規則
1313
14141. Constraints
15152. Align
16163. Anchors
17174. ChildSizing.Layout
18185. AutoSize
19196. OnResize, OnChangeBounds - 要注意的是,如果你設定的 bounds 和上述規則有衝突,那麼程式會進入無窮迴圈。
2020
2121## 常用屬性
2222
2323一些用來配置 autosizeing 的重要屬性:
2424
2525- Left, Top, Width, Height
2626- AutoSize: autosize 另 LCL 自動地更改 control 的寬和高。
2727- Anchors: 創建一個錨。例如,將一個 ComboBox 錨在一個 Label 的右邊。
2828- Align
2929- Constraints: 設定寬和高的最小值和最大值。
3030- BorderSpacing: 設定被錨住的 control 之間的空間。
3131- ChildSizing: 設定子 control 的 layout 和空間。
3232
3333上述法則的內部原理在此有描述: [[LCL AutoSizing]].
3434
3535# Fixed design
3636
3737這是預設設定. Anchors 屬性會被設定成 \[akLeft, akTop\], 也就是說, LCL 不會改變 Left, Top. 如果 AutoSize 為 false, 那麼 Width 和 Height 也會保持不便。如果 AutoSize 為 true, 那麼 Width 和 Height 會改動以適用內容。例如, TLabel.AutoSize 預設是 true, 因此,改變 Caption 就會重設 label 的大小. TButton.AutoSize 預設為 false, 所以改變 Caption 不會重設按鈕的大小。當你將 Button.AutoSize 設成 true 後,這個按鈕會在每次 Caption, 字體,主題改變後縮小或增大。要注意的是,這種改變不總是立即進行的。例如,在 FormCreate 時,所有 autosizing 動作會懸掛。 你可以在任何時候改變 Left, Top, Width, Height 屬性。
3838
3939# AutoSize
4040
4141`AutoSize` 這個布林屬性出現在許多類中;它允許 control 自動進行大小的調整以適應它包含的文本或圖形,也允許最有效地使用可用的空間。這是創建跨平台窗體的一個非常重要的機制。
4242
4343通常, AutoSize=true 後,會為可見 control:
4444
4545- 如果可能,它改變 control 的大小到 preferred size. 例如,一個 TButton 的寬度和高度被改變大小以適應它的 caption, 而對於 TEdit, 只改變高度. TEdit 的寬度不會自動改變大小。你可以手工改變它。可參考下 GetPreferredSize.
4646- 它同時移動所有固定位置的子 control。最左邊的子 control 會移到 Left=0 (依賴於 BoarderSpacing), 最上邊的子 control 會移到 Top=0.
4747
4848## Difference to Delphi
4949
5050Delphi 的 AutoSize 僅在特定的屬性改變時作用。例如,當 TLabel 的 Font 被改變時. LCL 的 AutoSize 總是有效的。例如,在 Delphi 裡,一個 AutoSize=true 的 Label 仍然可以設置大小(可能造成部分或全部 Caption 無法顯示), 這在 LCL 中是不會出現的,因為 AutoSize 總是起作用。另外,隱藏的 controls 不會被改變大小。
5151
5252## AutoSize and resizing the control
5353
5454當 AutoSize=false 按鈕就會呈現固定的大小。
5555
56-<figure>
57-<img src="Autosize1.png" title="Autosize1.png" />
58-<figcaption>Autosize1.png</figcaption>
59-</figure>
56+![](/wiki-images/1/1f/Autosize1.png)
6057
6158當對 button 設置 AutoSize=true, 那麼這些 buttons 就會被增大或縮小以適應文字或主題框架。
6259
63-<figure>
64-<img src="Autosize_on.png" title="Autosize_on.png" />
65-<figcaption>Autosize_on.png</figcaption>
66-</figure>
60+![](/wiki-images/7/75/Autosize_on.png)
6761
6862如上圖的 OK 按鈕所示, AutoSize 並不會將按鈕縮小到儘可能小。它使用GetPreferredSize 來獲取建議大小,這個函數又會調用 CalculatePreferredSize. 預設的 TWinControl 實作會查詢 widgetset, 它可能設定了建議的 width 和 height. 每個 control 都能覆寫 CalculatePreferredSize. 例如, TImage 就覆寫它並返回圖片的大小。如果沒有沒有建議的 width(或 height), 那麼就返回 0, LCL 會保持 width(height), 除非 ControlStyle 標記 csAutoSize0x0 被設置(這個標記當前只被 TPanel 使用).
6963
7064TWinControl 計算它所有的 controls 的大小,然後利用這個值來計算它自己的建議大小。
7165
7266當一個 control 的左錨和右錨都錨到其他對象,那麼它的 *寬度就是固定的*(動態的固定,依錨住的對象的位置變動而改變)。例如,當 *Align=alTop*(Align 屬性可看作一個快速設置錨的屬性,會覆蓋錨的設置), 這個 control 就的左錨就錨住它的父對象的到左邊界,右錨就錨住它的父對象的右邊界。之後它的寬度就隨父對象而改變了。如果 *Parent.AutoSize* 是 true 的話,那麼 *Parent* 將會實用這個 control 的建議寬度來計算它自己的建議寬度,參閱 [[Autosize / Layout|Align and AutoSize]]. 如果沒有可用的建議寬度,那麼就使用最後設置的邊界(**BaseBounds** 或者 **ReadBounds**). 如果邊界為設置,那麼就調用 **GetControlClassDefaultSize** 方法。對於 **Height** 也是同樣的機制。
7367
7468Constraints(約束)總是起作用,且優先級更高。
7569
7670## AutoSize and moving child controls
7771
7872When AutoSize=false you can place and move controls freely:
7973
80-<figure>
81-<img src="Autosize_on.png" title="Autosize_on.png" />
82-<figcaption>Autosize_on.png</figcaption>
83-</figure>
74+![](/wiki-images/7/75/Autosize_on.png)
8475
8576When AutoSize=true child controls with fixed positions are moved to fit.
8677
87-<figure>
88-<img src="Autosize_panel_on.png" title="Autosize_panel_on.png" />
89-<figcaption>Autosize_panel_on.png</figcaption>
90-</figure>
78+![](/wiki-images/d/d9/Autosize_panel_on.png)
9179
9280Both 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.
9381
9482Only child controls with the following values are moved:
9583
9684- Anchors=\[akLeft,akRight\]
9785- AnchorSide\[akLeft\].Control=nil
9886- AnchorSide\[akTop\].Control=nil
9987- Align=alNone
10088
10189The 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. For example TToolBar overrides that and defines a flow layout where controls that do not fit are put into subsequent lines.
10290
10391Controls can disable the moving of child controls by setting the ControlStyle flag csAutoSizeKeepChildLeft and csAutoSizeKeepChildTop (since 0.9.29).
10492
10593## AutoSize and Forms
10694
10795Forms 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 widgetset 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 widgetset resizes the form.
10896
10997That 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.
11098
11199### Force an auto sizing of a form
112100
113101You can start/execute a new AutoSize by doing:
114102
115103``` pascal
116104Form1.AutoSize := False;
117105Form1.AutoSize := True;
118106```
119107
120108### Compute the size of an autosized form in advance
121109
122110When 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:
123111
124112``` pascal
125113From1.HandleNeeded;
126114Form1.GetPreferredSize(PreferredWidth,PreferredHeight);
127115```
128116
129117**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.
130118
131119# Anchor Sides
132120
133121Controls 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.
134122
135123## Anchored to Parent or Nil
136124
137125Anchoring 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.
138126
139127There are four combinations of akLeft,akRight (akTop,akBottom):
140128
141129- **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.
142130- **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.
143131- **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.
144132- **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.
145133
146134### Changing the size of the parent of an anchored control
147135
148136A GroupBox with a Button: ![](/wiki-images/6/60/Anchors1.png)
149137
150138When the GroupBox is enlarged the distance of the anchored side is kept:
151139
152140With akLeft, no akRight: ![](/wiki-images/0/05/Anchors_akLeft.png)
153141
154142With akLeft and akRight: ![](/wiki-images/6/66/Anchors_akLeft_akRight.png)
155143
156144With akRight, no akLeft: ![](/wiki-images/d/d3/Anchors_akRight_no_akLeft.png)
157145
158146Three buttons in a groupbox: ![](/wiki-images/4/48/Anchors_no_akLeft_no_akRight_small.png)
159147
160148With no akLeft and no akRight their centers are scaled: ![](/wiki-images/7/7b/Anchors_no_akLeft_no_akRight_big.png)
161149
162150### Changing the size of an anchored control
163151
164152When 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:
165153
166154#### Anchored to nil
167155
168156![](/wiki-images/b/bd/Anchored_right_nil_resize1.png) A Button1 anchored \[akTop,akRight\], AnchorSide\[akRight\].Control=nil
169157
170158![](/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.
171159
172160![](/wiki-images/1/16/Anchored_right_nil_resize3.png) When the Groupbox is resized the button will keep the new distance.
173161
174162**Explanation:** setting the **Width** is equivalent to calling SetBounds(**Left**,Top,**NewWidth**,Height). That's why the *Left* is kept. This is Delphi compatible.
175163
176164#### Anchored to Parent
177165
178166![](/wiki-images/4/45/Anchored_right_parent_resize1.png) A Button1 anchored \[akTop,akRight\], AnchorSide\[akRight\].Control=Button1.Parent
179167
180168![](/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*.
181169
182170## Anchored to sibling
183171
184172You can anchor to neighbor controls. The following example shows:
185173
186174- you can anchor a label's left to the left of a button
187175- anchor a label's top to the bottom of a button
188176- anchor the center of a label to the center of a button
189177
190-<figure>
191-<img src="Anchorsides_example1.png" title="Anchorsides_example1.png" />
192-<figcaption>Anchorsides_example1.png</figcaption>
193-</figure>
178+![](/wiki-images/e/ee/Anchorsides_example1.png)
194179
195-<figure>
196-<img src="Anchorside_example2.png" title="Anchorside_example2.png" />
197-<figcaption>Anchorside_example2.png</figcaption>
198-</figure>
180+![](/wiki-images/b/ba/Anchorside_example2.png)
199181
200182For more details and how to setup anchors see: [[Anchor Sides]].
201183
202-<figure>
203-<img src="Anchoreditor.png" title="Anchoreditor.png" />
204-<figcaption>Anchoreditor.png</figcaption>
205-</figure>
184+![](/wiki-images/6/6d/Anchoreditor.png)
206185
207186## BorderSpacing
208187
209188The BorderSpacing properties controls the minimum amount of space around a control. The properties are:
210189
211190- Around: this amount in pixel is added to Left, Top, Right, Bottom.
212191- Left: space in pixel on the left side of the control
213192- Top: space in pixel above the control
214193- Right: space in pixel on the right side of the control
215194- Bottom: space in pixel below the control
216195- 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.
217196- 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.
218197- CellAlignVertical: same as CellAlignHorizontal but for vertical alignment.
219198
220199### BorderSpacing rules
221200
222201- Around is added to Left,Top,Right,Bottom
223202- The horizontal space between two controls (LeftControl, RightControl on Parent) is the maximum of
224203 - LeftControl.BorderSpacing.Right + LeftControl.BorderSpacing.Around
225204 - RightControl.BorderSpacing.Left + RightControl.BorderSpacing.Around
226205 - Parent.ChildSizing.HorizontalSpacing
227206
228207For 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.
229208
230209Note: The space can be even bigger, if the controls have constraints that don't allow to expand.
231210
232211A common example is anchoring a Label to an Edit.
233212
234-<figure>
235-<img src="BorderSpacing_Anchors.png" title="BorderSpacing_Anchors.png" />
236-<figcaption>BorderSpacing_Anchors.png</figcaption>
237-</figure>
213+![](/wiki-images/1/1e/BorderSpacing_Anchors.png)
238214
239215The 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.
240216
241217- The left space between a control and its parent (Label1 on GroupBox1) is the maximum of
242218 - Label1.BorderSpacing.Left + Label1.BorderSpacing.Around
243219 - GroupBox1.ChildSizing.LeftTopSpacing
244220
245221- The right space between a control and its parent (Edit1 on GroupBox1) is the maximum of
246222 - Edit1.BorderSpacing.Right + Edit1.BorderSpacing.Around
247223 - GroupBox1.ChildSizing.RightBottomSpacing
248224
249225- 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.
250226
251227- 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 space is the maximum of
252228 - Control1.BorderSpacing.Left + Control1.BorderSpacing.Around
253229 - Control2.BorderSpacing.Left + Control2.BorderSpacing.Around
254230
255231Here is a more complex example:
256232
257-<figure>
258-<img src="Borderspacing_anchors2.png" title="Borderspacing_anchors2.png" />
259-<figcaption>Borderspacing_anchors2.png</figcaption>
260-</figure>
233+![](/wiki-images/9/9c/Borderspacing_anchors2.png)
261234
262235The important parts of the lfm code:
263236
264237``` pascal
265238 object GroupBox1: TGroupBox
266239 AutoSize = True
267240 Caption = 'GroupBox1'
268241 TabOrder = 0
269242 object Label1: TLabel
270243 AnchorSideLeft.Control = GroupBox1
271244 AnchorSideTop.Control = Edit1
272245 AnchorSideTop.Side = asrCenter
273246 BorderSpacing.Around = 6
274247 Caption = 'Label1'
275248 end
276249 object Edit1: TEdit
277250 AnchorSideLeft.Control = Label1
278251 AnchorSideLeft.Side = asrBottom
279252 AnchorSideTop.Control = GroupBox1
280253 AnchorSideRight.Control = GroupBox1
281254 AnchorSideRight.Side = asrBottom
282255 Anchors = [akTop, akLeft, akRight]
283256 BorderSpacing.Around = 6
284257 TabOrder = 0
285258 Text = 'Edit1'
286259 end
287260 object Label2: TLabel
288261 AnchorSideLeft.Control = GroupBox1
289262 AnchorSideTop.Control = Edit1
290263 AnchorSideTop.Side = asrBottom
291264 BorderSpacing.Around = 6
292265 Caption = 'Label2'
293266 end
294267 object ComboBox1: TComboBox
295268 AnchorSideLeft.Control = Label2
296269 AnchorSideTop.Control = Label2
297270 AnchorSideTop.Side = asrBottom
298271 AnchorSideRight.Control = GroupBox1
299272 AnchorSideRight.Side = asrBottom
300273 Anchors = [akTop, akLeft, akRight]
301274 BorderSpacing.Right = 6
302275 TabOrder = 1
303276 Text = 'ComboBox1'
304277 end
305278 object CheckBox1: TCheckBox
306279 AnchorSideLeft.Control = GroupBox1
307280 AnchorSideTop.Control = ComboBox1
308281 AnchorSideTop.Side = asrBottom
309282 BorderSpacing.Around = 6
310283 Caption = 'CheckBox1'
311284 TabOrder = 2
312285 end
313286 object Label3: TLabel
314287 AnchorSideLeft.Control = GroupBox1
315288 AnchorSideTop.Control = Edit2
316289 AnchorSideTop.Side = asrCenter
317290 BorderSpacing.Around = 6
318291 Caption = 'Label3'
319292 end
320293 object Edit2: TEdit
321294 AnchorSideLeft.Control = Label3
322295 AnchorSideLeft.Side = asrBottom
323296 AnchorSideTop.Control = CheckBox1
324297 AnchorSideTop.Side = asrBottom
325298 BorderSpacing.Around = 6
326299 TabOrder = 3
327300 Text = 'Edit2'
328301 end
329302 end
330303```
331304
332305### BorderSpacing and Align
333306
334307BorderSpacing works with Align. In the example below there is a Memo1 with Align=alTop and a Memo2 with Align=alCLient.
335308
336309- Normally the two Memos would fill the whole GroupBox.
337310- But the Memo1 has BorderSpacing.Around=10, so there 10 pixel space around Memo1.
338311- The Memo2 has BorderSpacing.Top=20. The space between Memo1 and Memo2 is the maximum, which is the 20 from Memo2.
339312- Memo2 has also BorderSpacing.Right=50 so there is 50 Pixel space right of Memo2.
340313- The GroupBox can define default space for all its child controls via ChildSizing.LeftRightSpacing/VerticalSpacing/HorizontalSpacing. In this example it does not (all 0).
341314
342-<figure>
343-<img src="Borderspacing_align1.png" title="Borderspacing_align1.png" />
344-<figcaption>Borderspacing_align1.png</figcaption>
345-</figure>
315+![](/wiki-images/1/13/Borderspacing_align1.png)
346316
347317# Align
348318
349319The 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.
350320
351321The algorithm works as follows:
352322
353323- 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.
354324- Then all controls with alBottom are put to the bottom of the client area. Otherwise it works analog to alTop.
355325- 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.
356326- 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.
357327- If there is a control with alClient it will fill the remaining client.
358328- 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.
359329
360-<figure>
361-<img src="Autosize_align.png" title="Autosize_align.png" />
362-<figcaption>Autosize_align.png</figcaption>
363-</figure>
330+![](/wiki-images/e/e6/Autosize_align.png)
364331
365332## Align and BorderSpacing
366333
367334The space of BorderSpacing and of the parent's ChildSizing is applied to aligned controls. The memo below has Align=alClient.
368335
369-<figure>
370-<img src="Autosize_align_borderspacing.png" title="Autosize_align_borderspacing.png" />
371-<figcaption>Autosize_align_borderspacing.png</figcaption>
372-</figure>
336+![](/wiki-images/b/b4/Autosize_align_borderspacing.png)
373337
374338## Align and Anchoring
375339
376340The 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.
377341
378342## Align and AutoSize
379343
380344A 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*.
381345
382-<figure>
383-<img src="Autosize_align_autosize.png" title="Autosize_align_autosize.png" />
384-<figcaption>Autosize_align_autosize.png</figcaption>
385-</figure>
346+![](/wiki-images/3/35/Autosize_align_autosize.png)
386347
387348## Align and parent AutoSize
388349
389350A 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?
390351
391-<figure>
392-<img src="Autosize_nested1.png" title="Autosize_nested1.png" />
393-<figcaption>Autosize_nested1.png</figcaption>
394-</figure>
352+![](/wiki-images/b/b1/Autosize_nested1.png)
395353
396354The LCL uses the *preferred size* of the buttons and expands or shrinks the groupbox accordingly:
397355
398-<figure>
399-<img src="Autosize_nested2.png" title="Autosize_nested2.png" />
400-<figcaption>Autosize_nested2.png</figcaption>
401-</figure>
356+![](/wiki-images/2/28/Autosize_nested2.png)
402357
403358## alCustom
404359
405360This 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.
406361
407362## Order of controls with same Align
408363
409364Controls with same Align are added 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.
410365
411366Note: 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.
412367
413368# Layout
414369
415370## Rows, columns and lines
416371
417372You can align child controls in rows and columns using the ChildSizing properties. For example:
418373
419374- ChildSizing.Layout=cclLeftToRightThenTopToBottom
420375- ChildSizing.ControlsPerLine=3
421376- AutoSize=false (the groupbox does not fit the childs)
422377
423-<figure>
424-<img src="Autosize_layout_lefttoright.png" title="Autosize_layout_lefttoright.png" />
425-<figcaption>Autosize_layout_lefttoright.png</figcaption>
426-</figure>
378+![](/wiki-images/1/10/Autosize_layout_lefttoright.png)
427379
428380The **Layout** property defaults to cclNone. If you set **Layout** to another value, every child, that has *normal* anchors, will be aligned. *Normal* anchors means:
429381
430382- Anchors=\[akLeft,akTop\]
431383- AnchorSide\[akLeft\].Control=nil
432384- AnchorSide\[akTop\].Control=nil
433385- Align=alNone
434386
435387The 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.
436388
437389You 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.
438390
439391## Fixed space between rows and columns
440392
441393You can add space between with these properties:
442394
443395- ChildSizing.VerticalSpacing - Space between rows
444396- ChildSizing.HorizontalSpacing - Space between columns
445397- ChildSizing.LeftRightSpacing - Space on the left and right of *all* columns
446398- ChildSizing.TopBottomSpacing - Space above and below of *all* columns
447399
448400The above example with ChildSizing.VerticalSpacing=6, ChildSizing.HorizontalSpacing=15, ChildSizing.LeftRightSpacing=30, ChildSizing.TopBottomSpacing=10, AutoSize=true
449401
450-<figure>
451-<img src="Autosize_layout_parentspacing.png" title="Autosize_layout_parentspacing.png" />
452-<figcaption>Autosize_layout_parentspacing.png</figcaption>
453-</figure>
402+![](/wiki-images/a/a9/Autosize_layout_parentspacing.png)
454403
455404Additionally you can add individual space for each control with its BorderSpacing properties.
456405
457406## Enlarge
458407
459408The 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.
460409
461410- crsAnchorAligning - do not use the extra space
462411- crsScaleChilds - multiply the width/height with the same factor
463412- crsHomogeneousChildResize - add to each width/height the same amount
464413- crsHomogeneousSpaceResize - add to each space between the childs the same amount
465414
466415### crsScaleChilds
467416
468417ChildSizing.EnlargeHorizontal=crsScaleChilds, ChildSizing.EnlargeVertical=crsScaleChilds, AutoSize=false
469418
470-<figure>
471-<img src="Autosize_layout_scalechilds.png" title="Autosize_layout_scalechilds.png" />
472-<figcaption>Autosize_layout_scalechilds.png</figcaption>
473-</figure>
419+![](/wiki-images/b/b0/Autosize_layout_scalechilds.png)
474420
475421For example if the ClientWidth is twice as big as needed, then every child will be twice as big.
476422
477423### crsHomogeneousChildResize
478424
479425ChildSizing.EnlargeHorizontal=crsHomogeneousChildResize, ChildSizing.EnlargeVertical=crsHomogeneousChildResize, AutoSize=false
480426
481-<figure>
482-<img src="Autosize_layout_homogeneouschildresize.png" title="Autosize_layout_homogeneouschildresize.png" />
483-<figcaption>Autosize_layout_homogeneouschildresize.png</figcaption>
484-</figure>
427+![](/wiki-images/3/39/Autosize_layout_homogeneouschildresize.png)
485428
486429For example if the ClientWidth is 30 pixel bigger than needed, then every child will be 10 pixel broader.
487430
488431### crsHomogeneousSpaceResize
489432
490433ChildSizing.EnlargeHorizontal=crsHomogeneousSpaceResize, ChildSizing.EnlargeVertical=crsHomogeneousSpaceResize, AutoSize=false
491434
492-<figure>
493-<img src="Autosize_layout_homogeneousspaceresize.png" title="Autosize_layout_homogeneousspaceresize.png" />
494-<figcaption>Autosize_layout_homogeneousspaceresize.png</figcaption>
495-</figure>
435+![](/wiki-images/9/96/Autosize_layout_homogeneousspaceresize.png)
496436
497437For example if the ClientWidth is 40 pixel bigger than needed, there will be 10 pixel space on the left, right and between each child.
498438
499439## Shrink
500440
501441Shrinking works similar to enlarging. You can set different modes if there is not enough space for controls. **ShrinkHorizontal**, **ShrinkVertical**.
502442
503443## Individual cells
504444
505445In 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**.
506446
507447For example set the *BorderSpacing.CellAlignHorizontal* of the fifth button to **caCenter** you will get this:
508448
509-<figure>
510-<img src="Autosize_layout_cellhorizontal_cacenter.png" title="Autosize_layout_cellhorizontal_cacenter.png" />
511-<figcaption>Autosize_layout_cellhorizontal_cacenter.png</figcaption>
512-</figure>
449+![](/wiki-images/d/d3/Autosize_layout_cellhorizontal_cacenter.png)
513450
514451There are four possible values for CellAlignHorizontal/CellAlignVertical:
515452
516453- caFill: the child control fills the whole width (height) of the cell
517454- caCenter: the child control uses its preferred width (height) and will be centered in the cell
518455- caLeftTop: the child control uses its preferred width (height) and will be leftaligned in the cell
519456- caRightBottom: the child control uses its preferred width (height) and will be rightaligned in the cell
520457
521-<figure>
522-<img src="Autosize_layout_cellalign.png" title="Autosize_layout_cellalign.png" />
523-<figcaption>Autosize_layout_cellalign.png</figcaption>
524-</figure>
458+![](/wiki-images/c/c0/Autosize_layout_cellalign.png)
525459
526460# Custom layout with OnResize / OnChangeBounds
527461
528462Sometimes 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.
529463
530-<figure>
531-<img src="Autosize_onresize.png" title="Autosize_onresize.png" />
532-<figcaption>Autosize_onresize.png</figcaption>
533-</figure>
464+![](/wiki-images/4/4b/Autosize_onresize.png)
534465
535466Whenever the GroupBox is resized the ListBox1.Width should be one third. To achieve this set the OnResize event of the GroupBox1 to:
536467
537468``` pascal
538469procedure TForm1.GroupBox1Resize(Sender: TObject);
539470begin
540471 ListBox1.Width := GroupBox1.ClientWidth div 3;
541472end;
542473```
543474
544475### Common mistake: Wrong OnResize event
545476
546477Do 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.
547478
548479### Common mistake: Width instead of ClientWidth, AdjustClientRect
549480
550481The 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:
551482
552483``` pascal
553484procedure TForm1.Panel1Resize(Sender: TObject);
554485var
555486 r: TRect;
556487begin
557488 r := Panel1.ClientRect;
558489 Panel1.AdjustClientRect(r);
559490 ListBox1.Width := (r.Right - r.Left) div 3;
560491end;
561492```
562493
563494# Custom Controls
564495
565496When you write your own control, you can override and fine tune many parts of the LCL autosizing.
566497
567498## SetBounds, ChangeBounds, DoSetBounds
568499
569500**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.
570501
571502**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.
572503
573504**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.
574505
575506**DoAdjustClientRectChange** is called by the LCL and the LCL interface, when the ClientRect has changed and the Width and Height were kept.
576507
577508**WMSize** exists for Delphi/VCL compatibility. It is called by the LCL interface and on every change of bounds.
578509
579510## AdjustClientRect
580511
581512The 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).
582513
583514When 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**:
584515
585516``` pascal
586517 TCustomPanel = class(TCustomControl)
587518 ...
588519 protected
589520 ...
590521 procedure AdjustClientRect(var aRect: TRect); override;
591522 ...
592523
593524procedure TCustomPanel.AdjustClientRect(var aRect: TRect);
594525var
595526 BevelSize: Integer;
596527begin
597528 inherited AdjustClientRect(aRect);
598529
599530 BevelSize := BorderWidth;
600531 if (BevelOuter <> bvNone) then
601532 inc(BevelSize, BevelWidth);
602533 if (BevelInner <> bvNone) then
603534 inc(BevelSize, BevelWidth);
604535
605536 InflateRect(aRect, -BevelSize, -BevelSize);
606537end;
607538```
608539
609540### AdjustClientRect and Align
610541
611542AdjustClientRect 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:
612543
613-<figure>
614-<img src="Adjustclientrect_align.png" title="Adjustclientrect_align.png" />
615-<figcaption>Adjustclientrect_align.png</figcaption>
616-</figure>
544+![](/wiki-images/c/ca/Adjustclientrect_align.png)
617545
618546The Button1 in the screenshot was created with Align=alClient. ChildSizing.Layout is affected too.
619547
620548When anchoring to a parent via AnchorSides the AdjustClientRect is used too:
621549
622550``` pascal
623551 Button1.AnchorParallel(akTop,0,Button1.Parent);
624552```
625553
626554The 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).
627555
628556## Own AutoSize
629557
630558When *AutoSize* is set to true the control should be resized to the preferred size if possible.
631559
632560### Preferred Size
633561
634562The 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:
635563
636564``` pascal
637565 TQuadrat = class(TShape)
638566 protected
639567 procedure CalculatePreferredSize(var PreferredWidth,
640568 PreferredHeight: integer; WithThemeSpace: Boolean); override;
641569 end;
642570...
643571procedure TQuadrat.CalculatePreferredSize(var PreferredWidth,
644572 PreferredHeight: integer; WithThemeSpace: Boolean);
645573begin
646574 PreferredHeight:=Width;
647575end;
648576```
649577
650578The 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.
651579
652580**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.
653581
654582Computing 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:
655583
656584``` pascal
657585 TQuadrat = class(TShape)
658586 protected
659587 ...
660588 procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;
661589 end;
662590...
663591procedure TQuadrat.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);
664592begin
665593 inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);
666594 InvalidatePreferredSize;
667595 // Note: The AdjustSize can be omitted here, because the LCL does that after calling DoSetBounds.
668596end;
669597```
670598
671599The LCL will automatically trigger the autosizing when the bounds have changed, so the example is complete.
672600
673601The default TWinControl implementation of CalculatePreferredSize queries the widgetset, 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.
674602
675603### AdjustSize
676604
677605When the preferred size depends on a new property, then every time the property changes the auto sizing must be triggered. For example:
678606
679607``` pascal
680608procedure TQuadrat.SetSubTitle(const AValue: string);
681609begin
682610 if FSubTitle = AValue then exit;
683611 FSubTitle := AValue;
684612 InvalidatePreferredSize;
685613 AdjustSize;
686614end;
687615```
688616
689617# Reduce overhead with DisableAutoSizing, EnableAutoSizing
690618
691619Since 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 childs.
692620
693621Each time you change a property the LCL triggers a recompute of the layout:
694622
695623``` pascal
696624Label1.Caption := 'A'; // first recompute
697625Label2.Caption := 'B'; // second recompute
698626```
699627
700628The recompute will trigger events and send messages. To reduce the overhead you can suspend the autosizing:
701629
702630``` pascal
703631DisableAutoSizing;
704632try
705633 Label1.Caption := 'A'; // no recompute
706634 Label2.Caption := 'B'; // no recompute
707635finally
708636 EnableAutoSizing; // one recompute
709637end;
710638```
711639
712640The calls of Disable/EnableAutoSizing must be balanced. Only when EnableAutoSizing was called the same time as DisableAutoSizing the autosizing will start.
713641
714642Since 0.9.29 the Disable/EnableAutoSizing work for the whole form. This means every call of DisableAutoSizing suspends the autosizing for **all** controls on the same form. If you write your own control you can now use the following:
715643
716644``` pascal
717645procedure TMyRadioGroup.DoSomething;
718646begin
719647 DisableAutoSizing; // disables not only TMyRadioGroup, but the whole form
720648 try
721649 // delete items ...
722650 // add, reorder items ...
723651 // change item captions ...
724652 finally
725653 EnableAutoSizing; // recompute
726654 end;
727655end;
728656```
729657
730658Which basically means: you do not have to care. Just call Disable/EnableAutoSizing.
731659
732660Note: this is wrong:
733661
734662``` pascal
735663Button1.DisableAutoSizing;
736664Label1.EnableAutoSizing; // wrong: every control has its own counter
737665```
738666
739667## DisableAutoSizing and Form bounds
740668
741669DisableAutoSizing has another useful effect under asynchronous Window Managers like the ones under Linux. Each time a form is resized or moved the bounds are sent to the widgetset (DoSendBoundsToInterface). Even if the form is not shown the Handle is resized. The window manager often treats these bounds only as 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 widgetset making the form bounds more reliable.
742670
743671# Example: a button panel
744672
745673This 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.
746674
747675Step 1: Create the panel and set its Align property to alBottom. Add three TBitBtns.
748676
749-<figure>
750-<img src="Autosize_example_buttonpanel1.png" title="Autosize_example_buttonpanel1.png" />
751-<figcaption>Autosize_example_buttonpanel1.png</figcaption>
752-</figure>
677+![](/wiki-images/7/7e/Autosize_example_buttonpanel1.png)
753678
754679Set 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.
755680
756-<figure>
757-<img src="Autosize_example_buttonpanel2.png" title="Autosize_example_buttonpanel2.png" />
758-<figcaption>Autosize_example_buttonpanel2.png</figcaption>
759-</figure>
681+![](/wiki-images/4/49/Autosize_example_buttonpanel2.png)
760682
761683Set 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.
762684
763-<figure>
764-<img src="Autosize_example_buttonpanel3.png" title="Autosize_example_buttonpanel3.png" />
765-<figcaption>Autosize_example_buttonpanel3.png</figcaption>
766-</figure>
685+![](/wiki-images/b/b6/Autosize_example_buttonpanel3.png)
767686
768687The Panel height is still fixed. Now set the panel *AutoSize* property to *True*. The panel now shrinks vertically to fit the tallest button.
769688
770-<figure>
771-<img src="Autosize_example_buttonpanel4.png" title="Autosize_example_buttonpanel4.png" />
772-<figcaption>Autosize_example_buttonpanel4.png</figcaption>
773-</figure>
689+![](/wiki-images/3/36/Autosize_example_buttonpanel4.png)
774690
775691The *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.
776692
777-<figure>
778-<img src="Autosize_example_buttonpanel5.png" title="Autosize_example_buttonpanel5.png" />
779-<figcaption>Autosize_example_buttonpanel5.png</figcaption>
780-</figure>
693+![](/wiki-images/3/32/Autosize_example_buttonpanel5.png)
781694
782695Now add some space around the buttons. Set the panel's *ChildSizing.LeftTopSpacing/RightBottomSpacing/HorizontalSpacing* to *6*.
783696
784-<figure>
785-<img src="Autosize_example_buttonpanel6.png" title="Autosize_example_buttonpanel6.png" />
786-<figcaption>Autosize_example_buttonpanel6.png</figcaption>
787-</figure>
697+![](/wiki-images/4/46/Autosize_example_buttonpanel6.png)
788698
789699Finally clear the *Caption* of the panel and set its *BevelOuter* to *bvNone*.
790700
791-<figure>
792-<img src="Autosize_example_buttonpanel7.png" title="Autosize_example_buttonpanel7.png" />
793-<figcaption>Autosize_example_buttonpanel7.png</figcaption>
794-</figure>
701+![](/wiki-images/4/4e/Autosize_example_buttonpanel7.png)
795702
796703# Scrolling
797704
798705Some LCL controls like *TScrollBox*, *TForm*, *TFrame* allows to show scrollbars if the child controls do not fit. They inherit the behavior from their ancestor **TScrollingWinControl**.
799706
800707On a scrolling control the **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:
801708
802709``` pascal
803710ClientWidth <= Width
804711ClientHeight <= Height
805712```
806713
807714The **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*:
808715
809-<figure>
810-<img src="Autoscroll_all_fit1.png" title="Autoscroll_all_fit1.png" />
811-<figcaption>Autoscroll_all_fit1.png</figcaption>
812-</figure>
716+![](/wiki-images/d/db/Autoscroll_all_fit1.png)
813717
814718The 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.
815719
816720Now the panel is shrinked, so that the ClientWidth gets lower than 200:
817721
818-<figure>
819-<img src="Autoscroll_not_fit1.png" title="Autoscroll_not_fit1.png" />
820-<figcaption>Autoscroll_not_fit1.png</figcaption>
821-</figure>
722+![](/wiki-images/2/27/Autoscroll_not_fit1.png)
822723
823724The 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.
824725
825726## Scroll Position
826727
827728Changing the Position of a scrollbar does not change the Left, Top of child controls, does not change the logical client area and has no effect on autosizing. The child controls are only virtually moved.
828729
829730## Scrolling and AutoSize
830731
831732When AutoSize=true the LCL will expand the control to fit all child controls and no scrollbars are needed. If the control can not be expanded, then only the second action of AutoSize is executed: moving the child controls.
832733
833734# Docking
834735
835736Docking uses the described methods and properties of this page, see [[Docking]].
836737
837738# Splitter
838739
839740See [[TSplitter]].
840741
841742# TLabel.WordWrap
842743
843744**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.
844745
845746# DPI auto-adjustment and absolute layout auto-adjustment
846747
847748Historically 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 ok, but with the advent of LCL support for Android this could no longer be ignored. Starts 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.
848749
849750It is important to note that these new rules affect only controls which are positioned without Align and with the most standard Anchors only.
850751
851752In 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 positoning values will be expanded when the target DPI is larger then the design time DPI or reduced otherwise. The common value for desktop DPIs is 96 and is the default value given.
852753
853754property DesignTimeDPI: Integer read FDesignTimeDPI write FDesignTimeDPI;
854755
855756The way in which the layout is adjusted can be controlled with the property TApplication.LayoutAdjustmentPolicy
856757
857758` TLayoutAdjustmentPolicy = (`\
858759`   lapDefault,     // widgetset dependent`\
859760`   lapFixedLayout, // A fixed absolute layout in all platforms`\
860761`   lapAutoAdjustWithoutHorizontalScrolling, // Smartphone platforms use this one,`\
861762`                                            // the x axis is stretched to fill the screen and`\
862763`                                            // the y is scaled to fit the DPI`\
863764`   lapAutoAdjustForDPI // For desktops using High DPI, scale x and y to fit the DPI`\
864765` );`
865766
866767And 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 descendents of TControl react to this:
867768
868769``` pascal
869770TControl = class
870771public
871772...
872773 procedure AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;
873774 const AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth: Integer); virtual;
874775 function ShouldAutoAdjustLeftAndTop: Boolean; virtual;
875776 function ShouldAutoAdjustWidthAndHeight: Boolean; virtual;
876777```
877778
878779LCL-CustomDrawn-Android will call AutoAdjustLayout when the screen rotates, for example.
879780
880781# More details
881782
882783Many controls override *TControl.DoAutoSize* to perform the actual auto-sizing.
883784
884785IMPORTANT: Many Delphi controls override this method and many call this method directly after setting some properties.
885786
886787During handle creation not all interfaces can create complete Device Contexts which are needed to calculate things like text size.
887788
888789That's why you should always call *AdjustSize* instead of `DoAutoSize`.
889790
890791`TControl.AdjustSize` calls `DoAutoSize` in a smart fashion.
891792
892793During loading and handle creation the calls are delayed.
893794
894795This 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.
895796
896797When 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:
897798
898799The LCL will only autosize the *Width* (Height) if it is free to resize. In other words - the width is not autosized if:
899800
900801- 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.
901802- the *Width* is bound by the *Constraints* properties. The Contraints can also be overriden by the widgetset. For example the winapi does not allow resizing the height of a combobox. And the gtk widgetset does not allow resizing the width of a vertical scrollbar.
902803
903804Same for *Height*.
904805
905806The new size is calculated by the protected method **TControl.CalculatePreferredSize**. This method asks the widgetset 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.
906807
907808Some controls override this method. For example the TGraphicControl descendants like TLabel have no window handle and so cannot query the widgetset. They must calculate their preferred Width and Height themselves.
908809
909810The widgetsets must override the GetPreferredSize method for each widget class that has a preferred size (Width or Height or both).
910811
911812## Parent.AutoSize
912813
913814The above described the simple explanation. The real algorithm provides far more possibilities and is therefore far more complex.
914815
915816## Properties / Methods
916817
917818- Left
918819- Top
919820
920821If 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 widgetsets define a minimum/maximum somewhere around 10.000 or more.
921822
922823When the client area is scrolled the Left and Top are kept unchanged.
923824
924825During resizing/moving Left and Top are not always in sync with the coordinates of the Handle object.
925826
926827If Parent=nil then Left, Top depend on the widgetset 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.
927828
928829Hint: 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:
929830
930831``` pascal
931832with Button1 do
932833 SetBounds(NewLeft, NewTop, Width, Height);
933834```
934835
935836- Width
936837- Height
937838
938839The Size in pixels must not be negative, and most widgetsets 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.
939840
940841- BoundsRect
941842
942843Same as Bounds(Left, Top, Width, Height).
943844
944845Common newbie mistake:
945846
946847``` pascal
947848BoundsRect.Left := 3; // WRONG: common newbie mistake
948849```
949850
950851This has no effect, because reading BoundsRect is a function. It creates a temporary TRect on the stack. The above is the same as
951852
952853``` pascal
953854var
954855 r: TRect;
955856begin
956857 r := BoundsRect; // fetch the bounds
957858 r.Left := 3; // change a value on the stack
958859end; // no change
959860```
960861
961862- ClientRect
962863
963864Left 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.
964865
965866- ClientOrigin
966867
967868Returns 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.
968869
969870- LCLIntf.GetClientBounds
970871
971872Returns 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.
972873
973874- LCLIntf.GetWindowRect
974875
975876After 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.
976877
977878- FBaseBoundsLock: integer
978879
979880Increased/Decreased by LockBaseBounds/UnlockBaseBounds. Used to keep FBaseBounds during SetBounds calls.
980881
981882- FBaseParentClientSize: TPoint
982883
983884The 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.
984885
985886- FBoundsRectForNewParent: TRect
986887
987888When 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.
988889
989890- fLastAlignedBounds: TRect
990891
991892See TControl.SetAlignedBounds for an explanation. In short: It stops some circles between interface and LCL autosizing.
992893
993894- FLastChangebounds: TRect
994895
995896Used to stop calling ChangeBounds with the same coordinates. This happens very often.
996897
997898- FLastDoChangeBounds: TRect
998899
999900Used to avoid calling OnChangeBounds with the same coordinates. This reduces user defined autosizing.
1000901
1001902- FLastResizeClientHeight: integer
1002903- FLastResizeClientWidth: integer
1003904- FLastResizeHeight: integer
1004905- FLastResizeWidth: integer
1005906
1006907Used to avoid calling OnResize with the same coordinates. This reduces user defined autosizing.
1007908
1008909- FLoadedClientSize: TPoint
1009910
1010911During 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.
1011912
1012913- FReadBounds: TRect
1013914
1014915Same as FLoadedClientSize, but for SetLeft, SetTop, SetWidth, SetHeight.
1015916
1016917- procedure SetBoundsRectForNewParent(const AValue: TRect);
1017918
1018919Used to set FBoundsRectForNewParent. See above.
1019920
1020921- procedure SetAlignedBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;
1021922
1022923As SetBounds but without changing the default sizes.
1023924
1024925- procedure SetInitialBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;
1025926
1026927A smart version of SetBounds, reducing overhead during creation and loading.
1027928
1028929- procedure UpdateBaseBounds(StoreBounds, StoreParentClientSize, UseLoadedValues: boolean); virtual;
1029930
1030931Commit current bounds to base bounds.
1031932
1032933- procedure SetClientHeight(Value: Integer);
1033934- procedure SetClientSize(Value: TPoint);
1034935- procedure SetClientWidth(Value: Integer);
1035936
1036937Exists for Delphi compatibility too. Resizes the control, to get the wanted ClientRect size.
1037938
1038939- procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;
1039940
1040941This is the internal SetBounds. Applies constraints, updates base bounds, calls OnChangeBound, OnResize, locks bounds.
1041942
1042943- procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;
1043944
1044945This really sets the FLeft, FTop, FWidth, FHeight private variables.
1045946
1046947- procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;
1047948
1048949This is the standard procedure overriden by many Delphi controls. TWinControl overrides it too.
1049950
1050951- - ignores calls when bounds are locked
1051952 - lock the FBoundsRealized to avoid overhead to the interface during auto sizing.
1052953
1053954ChangeBounds is not locked this way.
1054955
1055956- Function GetClientOrigin: TPoint; virtual;
1056957
1057958Screen coordinate of Left, Top of client area.
1058959
1059960- Function GetClientRect: TRect; virtual;
1060961
1061962Size of client area. (always Left=0, Top=0)
1062963
1063964- Function GetScrolledClientRect: TRect; virtual;
1064965
1065966Visible client area in ClientRect.
1066967
1067968- function GetChildsRect(Scrolled: boolean): TRect; virtual;
1068969
1069970Returns 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).
1070971
1071972- function GetClientScrollOffset: TPoint; virtual;
1072973
1073974Returns the scrolling offset of the client area.
1074975
1075976- function GetControlOrigin: TPoint; virtual;
1076977
1077978Returns 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.
1078979
1079980# FAQ
1080981
1081982## Why does AutoSize not work in the designer properly?
1082983
1083984In 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.
1084985
1085986## Why does TForm.AutoSize not work when something changes?
1086987
1087988See [[Autosize / Layout|AutoSize and Forms]]
1088989
1089990## Do I need to call Application.ProcessMessages when creating lots of controls?
1090991
1091992Application.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:
1092993
1093994\<DELPHI\>procedure TFrom.Button1Click(Sender: TObject); begin
1094995
1095996` // change width of a control`\
1096997` Button1.Width := Button1.Width + 10;`\
1097998` // apply any needed changes and repaint the button`\
1098999` Application.ProcessMessages;`\
10991000` // do a lot of things that takes a long time`\
11001001` ...`\
11011002` // after leaving the OnClick the LCL automatically processes messages`
11021003
11031004end;\</DELPHI\>
11041005
11051006## When enabling Anchors at runtime the control resizes, does not use the current values. Why?
11061007
11071008akBottom 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.
11081009
11091010For 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:
11101011
11111012``` pascal
11121013ListBox1.UpdateBaseBounds(true, true, false);
11131014ListBox1.Anchors := ListBox1.Anchors + [akBottom];
11141015```
11151016
11161017Setting Anchors does not automatically call UpdateBaseBounds, because this would destroy the ability to change properties independently.
11171018
11181019## Resizing stringgrid columns in form's OnResize event does not work
11191020
11201021The 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.
11211022
11221023Solution: Use the TStringGrid's OnResize.
11231024
11241025## Why is TForm.Width equal to TForm.ClientWidth?
11251026
11261027Mattias' notes:
11271028
11281029"There are historical and technical reasons.
11291030
11301031For 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.
11311032
11321033For 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."
11331034
11341035## I get an infinite loop / How to debug autosizing?
11351036
11361037Here are some notes, what other users made wrong and how to find out:
11371038
11381039### Differences to Delphi
11391040
11401041For Delphi users: Please read: [[Autosize / Layout|Difference to Delphi]]
11411042
11421043### Overriding a LCL method and triggering a recompute
11431044
11441045You override a method and tell the LCL to compute again, even if nothing changed. Check for AdjustSize, Realign, AlignControls, InvalidatePreferredSize. For example:
11451046
11461047``` pascal
11471048procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);
11481049begin
11491050 // This will create an endless loop
11501051 InvalidatePreferredSize;
11511052
11521053 // This will create an endless loop too:
11531054 OtherComponent.Left:=10;
11541055 OtherComponent.Left:=20;
11551056
11561057 inherited SetBounds(ALeft, ATop, AWidth, AHeight);
11571058end;
11581059```
11591060
11601061Explanation: The SetBounds method is called often, even without changing anything. For example a "Left:=30;" will do so.
11611062
11621063Remedy: Check for changes:
11631064
11641065``` pascal
11651066procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);
11661067begin
11671068 if (Left <> ALeft) or (Top <> ATop) or (Width <> AWidth) or (Height <> AHeight) then
11681069 InvalidatePreferredSize;
11691070 inherited SetBounds(ALeft, ATop, AWidth, AHeight);
11701071end;
11711072```
11721073
11731074### OnResize/OnChangeBounds conflict with LCL properties
11741075
11751076You 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:
11761077
11771078``` pascal
11781079procedure TMainForm.FormResize(Sender: TObject);
11791080begin
11801081 // if Button1 is anchored or AutoSize=true then the following might create an endless loop:
11811082 Button1.Width:=100;
11821083end;
11831084```
11841085
11851086### LCL interface bug, custom widget
11861087
11871088Sometimes 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.
11881089
11891090### alCustom
11901091
11911092You 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]].
11921093
11931094# See also
11941095
11951096- [[Docking]]
11961097- [[Anchor Sides]]
11971098
11981099---
11991100*From the Free Pascal wiki, page "Autosize / Layout/zh TW", as it stood on 2020-02-09 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