Sign in Join
Wiki copytemporary

Autosize / Layout/ru: what changed

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

OldNewLine
11# Введение
22
33LCL может автоматически изменять размер и положение элемента управления, поэтому он адаптируется к изменениям шрифта, темы и текста или другого содержимого. Если вы хотите запустить программу на нескольких платформах, или если ваши подписи доступны на нескольких языках, то ваши средства управления должны правильно адаптироваться к своей среде. LCL позволяет не только сделать быстрый начальный дизайн (перемещение элементов управления на форму с помощью мыши), но и позже установить несколько ключевых свойств, которые сделают регуляторы автоматически адаптироваться впоследствии измененным содержанием и т.д.
44
55- Фиксированный дизайн (Fixed design): это значение по умолчанию при размещении элемента управления в дизайнере форм. Положение элемента управления фиксируется относительно его родителя. Размер и положение элемента управления (Left, Top) полностью изменяются программистом. Вы можете перемещать элемент с помощью мыши и изменять его размер свободно.
66- [[Autosize / Layout/ru|Выравнивание (Aligned)]]: выравненные элементы управления заполняют оставшееся пространство родителя вверху, снизу, слева или справа, или заполняют всё оставшееся пространство.
77- [[Autosize / Layout/ru|Привязка (Anchored)]]: вы можете закрепить стороны элемента управления (слева, сверху, справа, снизу) с его родителем или с другим элементот управления. Закрепление: LCL попытается оставаться на таком же расстоянии от точки привязки.
88- [[Autosize / Layout/ru|Разметка (Layout)]]: элементы управления могут быть автоматически выровнены по строкам и столбцам (например, TRadioGroup)
99- [[Autosize / Layout/ru|Пользовательская разметка через OnResize/OnChangeBounds]]: можно выровнять элементы управления самостоятельно в коде, используя события OnResize и OnChangeBounds.
1010- [[Autosize / Layout/ru|Пользовательские элементы управления]]: написав собственные элементы управления, вы можете переопределить почти каждое поведение LCL как вы хотите.
1111
1212## Правила приоритета
1313
14141. Constraints (Ограничения)
15152. Align (Выравнивание)
16163. Anchors(Привязки)
17174. ChildSizing.Layout
18185. AutoSize (Автомасштаб элемента)
19196. События OnResize, OnChangeBounds - однако, если вы установите границы, которые конфликтуют с вышеуказанными правилами, то это создаст бесконечный цикл
2020
2121## Общие свойства
2222
2323Чтобы настроить автоматическое изменение размера, можно изменить несколько основных свойств :
2424
2525- Left, Top, Width, Height
2626- AutoSize: Автомасштаб, указывает LCL автоматически изменить ширину и высоту элемента управления
2727- [[Autosize / Layout/ru|Anchors]]: позволяет создавать зависимости, например, чтобы привязать ComboBox к правой стороне \#Label.
2828- Align
2929- Constraints: позволяет установить минимум и максимум для ширины и высоты
3030- [[Autosize / Layout/ru|BorderSpacing]]: позволяет установить расстояние между привязанными элементами управления
3131- [[Autosize / Layout/ru|ChildSizing]]: позволяет установить расположение и расстояние дочерних элементов управления
3232
3333Внутренние алгоритмы объясняются здесь: [[LCL AutoSizing]].
3434
3535# Фиксированный дизайн
3636
3737Фиксированный ​​дизайн установлен по умолчанию. Якорь установлен в значение `[akLeft, akTop]`, что означает, значения Top и Left не будут изменяться LCL. Если значение `AutoSize` равно False, LCL не изменяет ширину или высоту. Если значение `AutoSize` равно True, то ширина и высота изменяются по размеру содержимого. Например, `TLabel.AutoSize` умолчанию True, поэтому при изменении текста в нем будет соответственно изменяться размер и `TLabel`, чтобы измененный текст поместился в контроле полностью. `TButton.AutoSize` умолчанию установлен в False, таким образом изменение надписи на кнопке не изменит размеры кнопки. При установке значения `Button.AutoSize` в True, кнопка будет уменьшаться или увеличиваться каждый раз при изменении `Caption` на кнопке или при изменении шрифта или темы. Обратите внимание, что это изменение не всегда происходит сразу. Например во время FormCreate всё автоизмение размера приостанавливается. В любой момент вы можете изменить свойства `Left`, `Top`, `Width` и `Height`.
3838
3939# Автомасштаб
4040
4141`AutoSize`(автомасштаб) - логическое свойство во многих классах; оно позволяет отрегулировать размер элемента управления автоматически, учитывать изменения в тексте или графике, содержащейся в нем, и позволяет наиболее эффективно использовать имеющееся пространство. Это очень важный механизм для создания кроссплатформенных форм.
4242
4343Обычно `AutoSize` равенTrue и делает две вещи для видимого элемента управления:
4444
4545- Если возможно, изменяет управление в нужный размер. Например ширина и высота `TButton` изменяется в соответствии с надписью, в то время как `TEdit` изменяется только в высоту. Ширина `TEdit` не изменяется автоматически. Вы можете изменить ширину `TEdit` самостоятельно.(см GetPreferredSize).
4646- Оно передвигает все фиксированно расположенные дочерние элементы управления так, чтобы самый левый дочерний элемент управления имел Left = 0 (зависит от значения свойства `BorderSpacing`) и самый верхний дочерний элемент управления имел Top = 0.
4747
4848## Отличия от Delphi
4949
5050- `AutoSize` в Delphi происходит только тогда, когда определенные свойства изменяются, например, когда шрифт из `TLabel` изменяется. В LCL `AutoSize` всегда активен. Delphi позволяет изменить размер `TLabel`, который имеет AutoSize = True, LCL - нет.
5151- Скрытые управления не изменяют размер автоматически.
5252- Изменение размера элемента управления не изменяет размер/расположение заякоренных(привязанных) дочерних элементов управления во время загрузки. Имейте в виду, что конструкторы элементов управления можно вызвать во время загрузки. При использовании `akRight`, `akBottom` якоря с установленными `AnchorSides` и `BorderSpacing` сохраняют правильное расстояние.
5353
5454## Автомасштаб и изменение размера элемента управления
5555
5656С помощью свойства кнопки `AutoSize=false` задаются фиксированный размер по умолчанию.
5757
58-<figure>
59-<img src="Autosize1.png" title="Autosize1.png" />
60-<figcaption>Autosize1.png</figcaption>
61-</figure>
58+![](/wiki-images/1/1f/Autosize1.png)
6259
6360При установке `AutoSize=true` для каждой кнопки, кнопки растягиваются (или сжимаются) в соответствии с текстом и темой.
6461
65-<figure>
66-<img src="Autosize_on.png" title="Autosize_on.png" />
67-<figcaption>Autosize_on.png</figcaption>
68-</figure>
62+![](/wiki-images/7/75/Autosize_on.png)
6963
7064`AutoSize` не сжимает элемент управления до минимально возможного размера, как вы можете видеть по кнопке OK. Он использует метод `GetPreferredSize` элемента управления, который вызывает метод `CalculatePreferredSize`. По умолчанию реализация `TWinControl` запрашивает виджетсет, который может иметь оптимальную ширину или высоту. Каждый элемент управления может переопределять метод `CalculatePreferredSize`. Например, `TImage` переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение равно 0, и [[LCL]] будет хранить ширину(высоту) элемента управления (если не установлен флаг `ControlStyle = csAutoSize`, который в настоящее время используется только `TPanel`).
7165
7266`TWinControl` вычисляет размер всех своих дочерних элементов управления и использует это для вычисления своего оптимального размера.
7367
7468Когда элемент управления привязан как слева, так и справа, его *ширина фиксирована*. Например, со значением привязки `Align=alTop` элемент управления привязан слева и справа, и соответствует ширине Родителя. Если `Parent.AutoSize` имеет значение true, тогда *Родитель* будет использовать оптимальную ширину элемента управления для вычисления своей собственной оптимальной ширины, и, таким образом, размер элемента управления будет изменен до его оптимальной ширины. См. [[Autosize / Layout/ru|Выравнивание и свойство AutoSize]]. Если значение оптимальной ширины недоступно, используются последние установленные границы (**BaseBounds** или **ReadBounds**). Если никаких ограничений не было установлено, используется метод `GetControlClassDefaultSize`. То же самое для свойства `Height` и привязки сверху и снизу.
7569
7670Ограничения применяются всегда и имеют приоритет.
7771
7872## Автомасштаб и перемещение дочерних элементов управления
7973
8074Когда *AutoSize=false*, вы можете размещать и перемещать элементы управления свободно:
8175
82-<figure>
83-<img src="Autosize_on.png" title="Autosize_on.png" />
84-<figcaption>Autosize_on.png</figcaption>
85-</figure>
76+![](/wiki-images/7/75/Autosize_on.png)
8677
8778Когда *AutoSize=true*, дочерние элементы управления с фиксированным положением перемещаются с подгонкой.
8879
8980------------------------------------------------------------------------
9081
9182Прим.переводчика: на самом деле, перемещаются границы родительского элемента управления так, чтобы дочерние элементы управления с фиксированной относительно друг друга позицией полностью умещались на родительском. При этом родительский элемент управления будет иметь минимально возможный размер. Таким образом, кнопки "перемещаются" относительно верхнего левого угла родительского элемента управления.
9283
9384------------------------------------------------------------------------
9485
95-<figure>
96-<img src="Autosize_panel_on.png" title="Autosize_panel_on.png" />
97-<figcaption>Autosize_panel_on.png</figcaption>
98-</figure>
86+![](/wiki-images/d/d9/Autosize_panel_on.png)
9987
10088Обе кнопки на панели были перемещены влево и кверху на одно и то же значение так, чтобы сверху и слева не оставалось свободного пространства. Если будут установлены свойства *BorderSpacing\>0* (у кнопок) или *Panel.ChildSizing.LeftRightSpacing\>0* (у панели), то кнопки будут перемещены так, чтобы предопределенный в упомянутых выше свойствах зазор был задействован.
10189
10290Могут быть перемещены дочерние элементы управления только со следующими свойствами:
10391
10492- Anchors=\[akLeft,akRight\]
10593- AnchorSide\[akLeft\].Control=nil
10694- AnchorSide\[akTop\].Control=nil
10795- Align=alNone
10896
10997Перемещение дочерних элементов управления зависит от свойства [[Autosize / Layout|ChildSizing.Layout]]. Разметка применяется в методе *TWinControl.AlignControls*, который может быть полностью или частично переопределен. Например, *TToolBar* переопределяет *ControlsAligned*, чтобы разместить все элементы управления со значением свойства *Align=alCustom*, и задает разметку для перемещения на следующие линии не "вписавшимся" в родительский контрол дочерним элементам управления.
11098
11199Родительские элементы управления могут отключать перемещение дочерних элементов управления установкой у свойства *ControlStyle* флагов *csAutoSizeKeepChildLeft* и *csAutoSizeKeepChildTop* (начиная с 0.9.29).
112100
113101## Автомасштаб и формы
114102
115103Формы без Parent родительского элемента управления управляются диспетчером окон и, следовательно, не могут свободно размещены или перемещены. Изменение размера в RunTime является лишь рекомендацией, и может быть проигнорировано диспетчером окон. Например, вы можете устанавливать ширину формы 1000 px, а widgetset в ответ изменит размер к 800 px. Если вы устанавливаете свойство *Witdh* в событии формы *OnResize*, то можете создать бесконечное зацикливание. Вот почему LCL TForm отключает *AutoSize*, когда widgetset изменяет размеры формы.
116104
117105Это означает, что действие свойства *AutoSize* для форм приостанавливается, когда пользователь изменяет размер формы или если диспетчеру окон не нравятся текущие границы окна. Например, некоторые диспетчеры окон в Linux имеют такие фишки, как "прилипающие" края, которые изменяют размеры текущего окна в привязке к другим окнам.
118106
119107### Принудительная установка автомасштабирования формы
120108
121109Вы можете выставить новое значение свойства *AutoSize*, выполнив:
122110
123111``` pascal
124112Form1.AutoSize := False;
125113Form1.AutoSize := True;
126114```
127115
128116### Расчет размера формы с "автомасштабом" заранее
129117
130118При размещении формы с "автомасштабом" (очевидно, имеется ввиду *Form1.AutoSize=True*) вам может потребоваться получить размер формы, прежде чем показывать ее. Для установки автомасштаба требуется дескриптор (хэндл окна). Вы можете рассчитать размер формы прежде, чем ее показывать, с помощью:
131119
132120``` pascal
133121Form1.HandleNeeded;
134122Form1.GetPreferredSize(PreferredWidth,PreferredHeight);
135123```
136124
137125> **Note:** Диспетчер окон и события формы могут изменять размер окна. Предпочтительный размер не включает рамку окна формы. Это запланированная особенность.
138126
139-\
140-\
141-
142127# Привязка сторон (Anchoring)
143128
144129Элементы управления для привязки к четырем сторонам имеют свойство *Anchors* с возможными значениями: *akLeft*, *akTop*, *akRight* и *akBottom*. Каждая сторона может быть привязана к родителю или к стороне другого собрата (элемент управления с одним и тем же родителем). Привязка означает **сохранение расстояния** между привязанными элементами управления. По умолчанию значение свойства *Anchors = \[akLeft, akTop\]*. Вертикальные привязки не зависят от горизонтальных привязок. Некоторые свойства, такие как *Align* и *Parent.AutoSize* имеют более высокий приоритет, и могут изменять поведение.
145130
146131## Привязка к родителю или nil
147132
148133Привязка к nil (по умолчанию) имеет почти такой же эффект, как привязка к родителю. Оба пытаются сохранять расстояние до края клиентской области Родителя. Для привязки к Nil используется расстояние от последнего вызова SetBounds, в то время как для привязки к родительскому используется значение BorderSpacing.
149134
150135Существует четыре комбинации akLeft, akRight (akTop, akBottom):
151136
152137- **akLeft, no akRight**: **Left** контрола фиксирован и не может быть изменен LCL. Правая сторона не закреплена, поэтому она следует за левой стороной. Это означает, что **Width** также сохраняется.
153138- **akLeft and akRight**: **Left** элемента управления фиксирован и не может быть изменен LCL. Правая сторона привязана к правой стороне Родителя. Это означает, что если Родитель увеличен на 100 пикселей, тогда **Width** контрола также будет увеличена на 100 пикселей.
154139- **akRight, no akLeft**: левая сторона управления не закреплена, поэтому она будет следовать за ее правой стороной. Правая сторона закреплена. Это означает, что если Родитель увеличится на 100 пикселей, тогда контроле перемещается вправо на 100 пикселей.
155140- **no akLeft and no akRight**: ни одна из сторон не закреплена. Положение центра контрола масштабируется с родителем. Например, если контрол находится в середине родительского элемента, а родительский элемент увеличен на 100 пикселей, тогда контрол перемещается на 50 пикселей вправо.
156141
157142### Изменение размера родителя с привязанным контролом
158143
159144При изменении размера родителя все привязанные дочерние контролы перемещаются и/или изменяются по размеру сразу, пока не будет отключено свойство AutoSizing. Например, AutoSizing отключается во время загрузки формы и при создании формы.
160145
161146GroupBox с кнопкой: ![](/wiki-images/6/60/Anchors1.png)
162147
163148Когда GroupBox увеличивается в размере, расстояние до привязанной стороны сохраняется:
164149
165150С akLeft, без akRight: ![](/wiki-images/0/05/Anchors_akLeft.png)
166151
167152С akLeft и akRight: ![](/wiki-images/6/66/Anchors_akLeft_akRight.png)
168153
169154С akRight, без akLeft: ![](/wiki-images/d/d3/Anchors_akRight_no_akLeft.png)
170155
171156Три кнопки в GroupBox: ![](/wiki-images/4/48/Anchors_no_akLeft_no_akRight_small.png)
172157
173158Без akLeft и без akRight их центры масштабируются: ![](/wiki-images/7/7b/Anchors_no_akLeft_no_akRight_big.png)
174159
175160**На заметку:**
176161
177162- Загрузка формы похожа на один большой SetBounds. Во время загрузки свойства задаются с использованием значений , сохраненных в файле формы lfm. Имейте в виду, что из-за предков и фреймов может быть несколько файлов lfm. После загрузки LCL активирует autosizing. Привязанные контролы используют ограничение в конце загрузки. Любой шаг между ними игнорируется.
178163- Для пользовательских контролов часто лучше устанавливать AnchorSides вместо Anchors.
179164
180165### Изменение размера привязанного элемента управления
181166
182167При изменении ширины привязанного элемента управления, например, через Object Inspector или через код *Button1.Width:=3*, вы можете увидеть разницу между привязкой к "родителю" и привязкой к "nil". Привязка к родителю будет изменять размер и перемещать Button1, а привязка к nil будет только изменять размер. Например:
183168
184169#### Привязка к nil
185170
186171![](/wiki-images/b/bd/Anchored_right_nil_resize1.png) Кнопка Button1 привязана \[akTop,akRight\], AnchorSide\[akRight\].Control=nil
187172
188173![](/wiki-images/a/a0/Anchored_right_nil_resize2.png) Установка ширины на меньшее значение приведет к сужению кнопки, сохраняя значение *Left* кнопки, увеличивая расстояние от правой стороны кнопки до правого края.
189174
190175![](/wiki-images/1/16/Anchored_right_nil_resize3.png) При изменении размера Groupbox кнопка сохранит новое расстояние до правого края.
191176
192177**Объяснение:** установка значения **Width** кнопки эквивалентна вызову SetBounds(**Left**,Top,**NewWidth**,Height). Вот почему значение **Left** сохраняется. Это совместимо с поведением контролов в Delphi.
193178
194179#### Привязка к родителю
195180
196181![](/wiki-images/4/45/Anchored_right_parent_resize1.png) Кнопка Button1 привязана \[akTop,akRight\], AnchorSide\[akRight\].Control=Button1.Parent
197182
198183![](/wiki-images/0/04/Anchored_right_parent_resize2.png) Установка значения *Width* на меньшее значение приведет к сужению кнопки, сохранив расстояние от ее правой границы до края компонента-родителя и изменив значение *Left* кнопки.
199184
200185## Привязка к соседу
201186
202187Вы можете привязываться к соседним элементам управления. Следующий пример показывает, что:
203188
204189- вы можете привязать левый край label'а к левому краю кнопки
205190- привязать верхний край label'а к нижнему краю кнопки
206191- привязать центр label'а к центру кнопки\
207192
208193![](/wiki-images/e/ee/Anchorsides_example1.png)\
209194![](/wiki-images/b/ba/Anchorside_example2.png)\
210-\
195+
211196Подробнее о том, как установить привязку, см. [[Anchor Sides/ru|Стороны привязки]].\
212-\
197+
213198![](/wiki-images/6/6d/Anchoreditor.png)
214199
215200## Зазор
216201
217202Свойство BorderSpacing(зазор) управляет минимальным количеством пространства вокруг элемента управления. Свойства:
218203
219204- **Around**: эта величина в пикселах, которая добавляется к каждому значению Left, Top, Right, Bottom элемента управления.
220205- **Left**: зазор в пикселах с левой стороны элемента управления
221206- **Top**: зазор в пикселах сверху элемента управления
222207- **Right**: зазор в пикселах с правой стороны элемента управления
223208- **Bottom**: зазор в пикселах под элементом управления
224209- **InnerBorder**: это зазор в пикселях, добавляется дважды к оптимальной ширине и высоте. Некоторые элементы управления переопределяют вычисление и игнорируют это свойство. Пример, где он работает, - TButton. С помощью InnerBorder вы можете сделать кнопку больше, чем необходимо.
225210- **CellAlignHorizontal**: Этот параметр используется в табличных макетах, таких как ChildSizing.Layout=cclLeftToRightThenTopToBottom. Если элемент управления меньше ячейки таблицы, это свойство определяет, как выровнять элемент управления: слева ccaLeftTop, справа ccaRightBottom или посередине ccaCenter.
226211- **CellAlignVertical**: так же, как CellAlignHorizontal, но для вертикального выравнивания.
227212
228213### Правила зазоров
229214
230215- Значение Around добавляется к зазору для Left,Top,Right,Bottom элемента управления.
231216- Зазор может быть еще больше, если элементы управления имеют ограничения, которые не позволяют им растягиваться.
232217
233218#### Привязка к противоположной стороне
234219
235220Например, правая сторона A к левой стороне B.
236221
237222Используются обе границы A и B.
238223
239224- Горизонтальный зазор между двумя элементами управления (LeftControl, RightControl на общем родителе) он максимален, если:
240225 - LeftControl.BorderSpacing.Right + LeftControl.BorderSpacing.Around
241226 - RightControl.BorderSpacing.Left + RightControl.BorderSpacing.Around
242227 - Parent.ChildSizing.HorizontalSpacing
243228- Вертикальный зазор работает аналогично: между двумя элементами управления (TopControl, BottomControl на общем родителе) он максимален, если:
244229 - TopControl.BorderSpacing.Bottom + TopControl.BorderSpacing.Around
245230 - BottomControl.BorderSpacing.Top + BottomControl.BorderSpacing.Around
246231 - Parent.ChildSizing.VerticalSpacing
247232
248233Например:
249234
250235- если LeftControl.BorderSpacing.Right=3 и LeftControl.BorderSpacing.Around=4, то между двумя элементами управления должно быть не менее 7px
251236- если RightControl.BorderSpacing.Left=4 и RightControl.BorderSpacing.Around=4, то пространство будет не менее 8px
252237- если Parent.ChildSizing.HorizontalSpacing=10, тогда пространство будет не менее 10px
253238
254239#### Привязка к этой же стороне
255240
256241Например, правая сторона A к правой стороне B.
257242
258243- Задействуется только зазор у границы А.
259244- Свойство Parent.ChildSizing.Horizontal/VerticalSpacing не используется.
260245
261246#### Привязка к центру
262247
263248Например, центр А привязан к центру В.
264249
265250Не задействуются ни зазор, ни свойство Parent.ChildSizing.Horizontal/VerticalSpacing.
266251
267252#### Пример
268253
269254Общим примером является привязка Label'а к Edit'у.\
270255![](/wiki-images/1/1e/BorderSpacing_Anchors.png)\
271256Центр Label'а вертикально привязан к Edit'у. Левая сторона Edit'а привязана к правой стороне Label'а. Оба имеют BorderSpacing.Around=6. Это приводит к наличию зазора в 6px между Label'ом и Edit'ом и наличию зазора в 6px слева от Label'а, а также наличию зазора в 6px справа от Edit'а. Также еще существует 6 пикселей зазора выше и ниже Edit'а.
272257
273258- Зазор слева между элементом управления и его родителем (Label1 к GroupBox1) является максимальным, когда
274259 - Label1.BorderSpacing.Left + Label1.BorderSpacing.Around
275260 - GroupBox1.ChildSizing.LeftTopSpacing
276261
277262- Зазор справа между элементом управления и его родителем (Edit1 к GroupBox1) является максимальным, когда
278263 - Edit1.BorderSpacing.Right + Edit1.BorderSpacing.Around
279264 - GroupBox1.ChildSizing.RightBottomSpacing
280265
281266- Когда центр элемента управления привязан к другому элементу управления, например, вышеуказанный Label центрирован по вертикали к Edit1, тогда все остальные зазоры игнорируются.
282267
283268- Когда левая сторона элемента управления привязана к левой стороне другого элемента управления (т.е., они выровнены по левой стороне), то расстояние между обеими левыми сторонами является
284269 - Control1.BorderSpacing.Left + Control1.BorderSpacing.Around
285270
286271Вот более сложный пример:
287272
288-<figure>
289-<img src="Borderspacing_anchors2.png" title="Borderspacing_anchors2.png" />
290-<figcaption>Borderspacing_anchors2.png</figcaption>
291-</figure>
273+![](/wiki-images/9/9c/Borderspacing_anchors2.png)
292274
293275Важные части кода lfm:
294276
295277``` pascal
296278 object GroupBox1: TGroupBox
297279 AutoSize = True
298280 Caption = 'GroupBox1'
299281 TabOrder = 0
300282 object Label1: TLabel
301283 AnchorSideLeft.Control = GroupBox1
302284 AnchorSideTop.Control = Edit1
303285 AnchorSideTop.Side = asrCenter
304286 BorderSpacing.Around = 6
305287 Caption = 'Label1'
306288 end
307289 object Edit1: TEdit
308290 AnchorSideLeft.Control = Label1
309291 AnchorSideLeft.Side = asrBottom
310292 AnchorSideTop.Control = GroupBox1
311293 AnchorSideRight.Control = GroupBox1
312294 AnchorSideRight.Side = asrBottom
313295 Anchors = [akTop, akLeft, akRight]
314296 BorderSpacing.Around = 6
315297 TabOrder = 0
316298 Text = 'Edit1'
317299 end
318300 object Label2: TLabel
319301 AnchorSideLeft.Control = GroupBox1
320302 AnchorSideTop.Control = Edit1
321303 AnchorSideTop.Side = asrBottom
322304 BorderSpacing.Around = 6
323305 Caption = 'Label2'
324306 end
325307 object ComboBox1: TComboBox
326308 AnchorSideLeft.Control = Label2
327309 AnchorSideTop.Control = Label2
328310 AnchorSideTop.Side = asrBottom
329311 AnchorSideRight.Control = GroupBox1
330312 AnchorSideRight.Side = asrBottom
331313 Anchors = [akTop, akLeft, akRight]
332314 BorderSpacing.Right = 6
333315 TabOrder = 1
334316 Text = 'ComboBox1'
335317 end
336318 object CheckBox1: TCheckBox
337319 AnchorSideLeft.Control = GroupBox1
338320 AnchorSideTop.Control = ComboBox1
339321 AnchorSideTop.Side = asrBottom
340322 BorderSpacing.Around = 6
341323 Caption = 'CheckBox1'
342324 TabOrder = 2
343325 end
344326 object Label3: TLabel
345327 AnchorSideLeft.Control = GroupBox1
346328 AnchorSideTop.Control = Edit2
347329 AnchorSideTop.Side = asrCenter
348330 BorderSpacing.Around = 6
349331 Caption = 'Label3'
350332 end
351333 object Edit2: TEdit
352334 AnchorSideLeft.Control = Label3
353335 AnchorSideLeft.Side = asrBottom
354336 AnchorSideTop.Control = CheckBox1
355337 AnchorSideTop.Side = asrBottom
356338 BorderSpacing.Around = 6
357339 TabOrder = 3
358340 Text = 'Edit2'
359341 end
360342 end
361343```
362344
363345### Зазор и выравнивание
364346
365347Зазор работает с выравниванием. В приведенном ниже примере есть Memo1 с Align=alTop и Memo2 с Align=alCLient.
366348
367349- Обычно два Memo заполняют весь GroupBox.
368350- Но Memo1 имеет BorderSpacing.Around=10, так что вокруг Memo1 будет присутствовать зазор в 10 пикселей.
369351- Memo2 имеет BorderSpacing.Top=20. Зазор между Memo1 и Memo2 будет максимальным, который от Memo1 до Memo2 составит 20px.
370352- Memo2 также имеет и BorderSpacing.Right=50, поэтому справа от Memo2 имеется зазор в 50 пикселей.
371353- GroupBox может задавать зазор по умолчанию для всех своих дочерних элементов управления через значения свойств ChildSizing.LeftRightSpacing/VerticalSpacing/HorizontalSpacing. В этом примере их нет (все упомянутые значения равны 0).\
372354
373355![](/wiki-images/1/13/Borderspacing_align1.png)\
374-\
375356
376357# Выравнивание (Aligned)
377358
378359Свойство Align(выравнивание) работает почти так же, как в Delphi, и может использоваться для быстрого заполнения какой-нибудь области. Например, чтобы TListBox заполнил всю область своего родителя, установите ListBox1.Align=alClient. Значения выравнивания alTop, alBottom, alLeft и alRight будут размещать элементы управления по возможности без перекрытия друг друга. Это означает, что все элементы управления свойства Align со значением *alLeft, alTop, alBottom, alRight* не будут перекрываться, если есть достаточно места.
379360
380361Алгоритм работает следующим образом:
381362
382363- Сначала все элементы управления с помощью значения свойства alTop помещаются в верхнюю часть клиентской области. Если имеется несколько элементов управления с помощью alTop, последний добавленный/перемещенный будет помещен выше всех. Алгоритм будет стараться избегать перекрытия и сохранять высоту элемента управления, пока ширина всех элементов остается максимальной. Стороны привязки левой, верхней и правой сторон игнорируются. Нижняя сторона привязки работает нормально. Зазор и значение свойства Parent.ChildSize просчитываются, поэтому вы можете задавать зазор вокруг каждого выровненного элемента управления.
383364- Затем все элементы управления с помощью значения свойства alBottom помещаются в нижнюю часть клиентской области. В противном случае оно работает аналогично alTop.
384365- Потом все элементы управления с помощью значения свойства alLeft помещаются слева от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.
385366- Затем все элементы управления с помощью значения свойства alRight помещаются справа от клиентской области между элементами управления alTop и alBottom. В противном случае оно работает аналогично alTop.
386367- Если есть элемент управления с установленным свойством alClient, он заполнит оставшуюся часть клиентской области.
387368- Если имеется более одного элемента управления с установленным свойством alClient, они будут помещаться в одно и то же положение, перекрывая друг друга. Используйте свойство Visibility, чтобы определить, какой из них будет показан.\
388369 \
389370
390-<figure>
391-<img src="Autosize_align.png" title="Autosize_align.png" />
392-<figcaption>Autosize_align.png</figcaption>
393-</figure>
371+![](/wiki-images/e/e6/Autosize_align.png)
394372
395373## Выравнивание и зазоры
396374
397375Величина свойства BorderSpacing и свойства ChildSize родителя применяется к выравниваемым элементам управления. Элемент управления memo ниже имеет Align=alClient.\
398376
399377------------------------------------------------------------------------
400378
401379Прим.переводчика: Ох, уж эти языковые особенности. Автор, очевидно, имел в виду следующее: даже если у дочернего контрола выравнивание выставлено как Align=alClient, то он не будет заполнять собой контрол-родитель полностью, если его свойство BorderSpacing или свойство ChildSize родителя имеют ненулевое значение.
402380
403381------------------------------------------------------------------------
404382
405-\
406383![](/wiki-images/b/b4/Autosize_align_borderspacing.png)
407384
408385## Выравнивание и Привязка
409386
410387Свободная сторона выровненного элемента управления (напр., правая сторона **Align=alLeft**) следует правилам привязки. Если привязка не установлена, тогда элемент управления будет сохранять значение своей **Width**. Если привязка установлена, то значение **Width** изменится.
411388
412389## Выравнивание и свойство AutoSize
413390
414391Элемент управления, выровненный посредством alLeft или alRight, растягивается по вертикали и будет использовать свою ширину, определенную на этапе проектирования формы. Если свойство *AutoSize* установлено в *true*, то значение параметра *Width* будет оптимальной шириной. Кнопка снизу имеет *Align=alRight* и *AutoSize=true*.\
415392![](/wiki-images/3/35/Autosize_align_autosize.png)
416393
417394## Выравнивание и свойство AutoSize родителя
418395
419396Элемент управления, выровненный посредством alClient, заполняет оставшееся пространство. Родительский элемент управления со значением свойства *AutoSize=true* при этом будет растягиваться или сжиматься так, чтобы впритирку умещать на себе свои дочерние элементы. Что произойдет, если вы объедините эти два свойства? Скажем, вы положили кнопку с Align=alClient на groupbox с AutoSize=true?\
420397![](/wiki-images/b/b1/Autosize_nested1.png)\
421398LCL использует *предпочтительный размер* кнопок и потому растягивает или сжимает groupbox:
422399
423400![](/wiki-images/2/28/Autosize_nested2.png)\
424401
425402## alCustom
426403
427404Это значение Align существует для пользовательских алгоритмов AutoSize и обрабатывается LCL почти как alNone. Элементы управления с установленным значением alCustom не перемещаются LCL, но могут быть перемещены вашим пользовательским элементом управления. Для этого вы должны переопределить методы `CalculatePreferredSize` и `DoAutoSize`.
428405
429406## Порядок расположения элементов управления с одинаковым значением Align
430407
431408Элементы управления с одинаковым выравниванием добавляются в следующем порядке. Для alLeft элемент управления с наименьшим значением Left, для alTop - наименьшим значением Top, для alRight - наибольшая величина Left+Width, для alBottom - наибольшая величина Top+Height. Если два элемента управления имеют одну и ту же координату, последний из них одерживает победу (вызывая SetBounds или SetParent). Элементы управления могут переопределять CreateControlAlignList, чтобы изменить порядок или переопределить DoAlignChildControls для обработки всего Align.
432409
433410> **Note:** до версии Lazarus 0.9.29 элементы управления размещались в Z-порядке. В некоторых случаях это приводило к переупорядочиванию элементов управления и должно было быть исправлено. Существует одна несовместимость: если вы добавили несколько элементов управления с выравниванием alTop или alLeft без указания границ, все элементы управления будут помещены в координаты 0.0, и, следовательно, последние добавленные окажутся поверх контролов, добавленных ранее.
434411
435-\
436-\
437-
438412# Разметка (Layout)
439413
440414## Строки, столбцы и строки
441415
442416Вы можете выравнивать дочерние элементы управления в строках и столбцах, используя свойства ChildSize. Например:
443417
444418- ChildSizing.Layout=cclLeftToRightThenTopToBottom
445419- ChildSizing.ControlsPerLine=3
446420- AutoSize=false (не изменяет размеры, чтобы подстраиваться под дочерние элементы управления)\
447421
448-<figure>
449-<img src="Autosize_layout_lefttoright.png" title="Autosize_layout_lefttoright.png" />
450-<figcaption>Autosize_layout_lefttoright.png</figcaption>
451-</figure>
422+![](/wiki-images/1/10/Autosize_layout_lefttoright.png)
452423
453424Свойство **Layout** по умолчанию \[имеет значение\] cclNone. Если вы установите **Layout** в другое значение, каждый дочерний элемент, имеющий *нормальные* привязки, будет выровнен. *Нормальные* привязки означают:
454425
455426- Anchors=\[akLeft,akTop\]
456427- AnchorSide\[akLeft\].Control=nil
457428- AnchorSide\[akTop\].Control=nil
458429- Align=alNone
459430
460431Значение **cclLeftToRightThenTopToBottom** будет помещать первый дочерний элемент управление вверху \[и\] слева, второй - \[вверху и\] правее, и так далее. Это - **линия**. Свойство **ControlsPerLine** определяет, когда начинается новая строка. В приведенном выше примере каждая линия (строка) имеет 3 элемента управления. Существует \[всего\] 12 элементов управления, поэтому есть 4 линии (строки), каждая из которых имеет \[по\] 3 элемента управления (столбцы). Если *ControlsPerLine* равен 0, это означает неограниченное количество элементов управления в строке - будет только одна строка со всеми дочерними элементами.
461432
462433Вы можете видеть, что строки имеют разные размеры, каждая строка имеет размер самого большого элемента управления и что элементы управления изменяют размеры до ширины столбца. Между строками нет пробела. Пространство в изображении определяется используемой темой, а не \[приходит\] из LCL.
463434
464435## Фиксированные зазоры между строками и столбцами
465436
466437Вы можете добавить зазоры между \[строками и столбцами\] этими свойствами:
467438
468439- ChildSizing.VerticalSpacing - зазор между строками
469440- ChildSizing.HorizontalSpacing - зазор между столбцами
470441- ChildSizing.LeftRightSpacing - зазор между левой и правой \[стороной\] *всех* столбцов
471442- ChildSizing.TopBottomSpacing - зазор выше и ниже *всех* столбцов
472443
473444Выше приведенный пример с \[параметрами\]:\
474445ChildSizing.VerticalSpacing=6,\
475446ChildSizing.HorizontalSpacing=15,\
476447ChildSizing.LeftRightSpacing=30,\
477448ChildSizing.TopBottomSpacing=10,\
478449AutoSize=true\
479450![](/wiki-images/a/a9/Autosize_layout_parentspacing.png)
480451
481452Кроме того, вы можете добавить зазор для каждого элемента управления отдельно с \[помощью\] его свойства BorderSpacing.
482453
483454## Растяжение
484455
485456Приведенный выше пример изменяет размер GroupBox до необходимых размеров. Если ваш GroupBox имеет фиксированный размер или если он не является свободно изменяемым, например, если GroupBox должен заполнять всю ширину формы, то дочерние элементы должны растягиваться. Существует несколько режимов. Режим по умолчанию *ChildSizing.EnlargeHorizontal=crsAnchorAligning* запрещает что-либо растягивать. Зазоры с правой стороны не будут использоваться.
486457
487458- `crsAnchorAligning` - не использует дополнительные зазоры
488459- `crsScaleChilds` - умножает ширину/высоту на тот же коэффициент
489460- `crsHomogeneousChildResize` - добавляет к каждой ширине/высоте ту же величину
490461- `crsHomogeneousSpaceResize` - добавляет к каждому зазору между дочерними элементами ту же величину
491462
492463### crsScaleChilds
493464
494465`ChildSizing.EnlargeHorizontal=crsScaleChilds`\
495466`ChildSizing.EnlargeVertical=crsScaleChilds`\
496467`AutoSize=false`\
497468![](/wiki-images/b/b0/Autosize_layout_scalechilds.png)
498469
499470Например, если значение ClientWidth вдвое больше необходимого, то каждый дочерний элемент будет в два раза больше.
500471
501472### crsHomogeneousChildResize
502473
503474`ChildSizing.EnlargeHorizontal=crsHomogeneousChildResize`\
504475`ChildSizing.EnlargeVertical=crsHomogeneousChildResize`\
505476`AutoSize=false`\
506477![](/wiki-images/3/39/Autosize_layout_homogeneouschildresize.png)
507478
508479Например, если ClientWidth на 30 пикселей больше, чем нужно, то каждый дочерний элемент будет на 10 пикселей шире.
509480
510481### crsHomogeneousSpaceResize
511482
512483`ChildSizing.EnlargeHorizontal=crsHomogeneousSpaceResize`\
513484`ChildSizing.EnlargeVertical=crsHomogeneousSpaceResize`\
514485`AutoSize=false`\
515486![](/wiki-images/9/96/Autosize_layout_homogeneousspaceresize.png)
516487
517488Например, если ClientWidth на 40 пикселов больше, чем необходимо, появится \[дополнительно по\] 10 пикселей зазора слева, справа и между каждым дочерним элементом.
518489
519490## Сжатие
520491
521492Сжатие работает аналогично растяжению. Вы можете установить разные режимы, если для элементов управления недостаточно места. **ShrinkHorizontal**, **ShrinkVertical**.
522493
523494## Отдельные ячейки
524495
525496В приведенных выше примерах все элементы управления изменялись одинаково, каждый заполнял всю *ячейку*. *Ячейка* - это пространство в определенной строке и столбце. Обычно элемент управления заполняет все пространство ячейки. Это можно изменить с помощью свойств **BorderSpacing.CellAlignHorizontal** и **BorderSpacing.CellAlignVertical**.
526497
527498Например, установите *BorderSpacing.CellAlignHorizontal* пятой кнопки в **caCenter** \[и\] вы получите следующее:
528499
529-<figure>
530-<img src="Autosize_layout_cellhorizontal_cacenter.png" title="Autosize_layout_cellhorizontal_cacenter.png" />
531-<figcaption>Autosize_layout_cellhorizontal_cacenter.png</figcaption>
532-</figure>
500+![](/wiki-images/d/d3/Autosize_layout_cellhorizontal_cacenter.png)
533501
534502Существует четыре возможных значения для CellAlign Horizontal/CellAlign Vertical:
535503
536504- `caFill`: дочерний элемент управления заполняет всю ширину (высоту) ячейки
537505- `caCenter`: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет центрироваться в ячейке
538506- `caLeftTop`: дочерний элемент управления использует свою оптимальную ширину (высоту) и выравнивается в ячейке слева
539507- `caRightBottom`: дочерний элемент управления использует свою оптимальную ширину (высоту) и будет в ячейке выравниваться по правому краю
540508
541509![](/wiki-images/c/c0/Autosize_layout_cellalign.png)\
542-\
543510
544511# Пользовательская разметка через OnResize/OnChangeBounds
545512
546513Иногда разметка LCL недостаточно \[оптимальна\]. В приведенном ниже примере показан GroupBox1 со списком ListBox1 и Memo1. ListBox1 должен заполнить одну треть пространства, Memo1 - забрать все остальное.\
547514![](/wiki-images/4/4b/Autosize_onresize.png)
548515
549516Всякий раз, когда размер GroupBox изменяется, ListBox1.Width должен составлять одну треть. Для этого определите событие OnResize для GroupBox1:
550517
551518``` pascal
552519procedure TForm1.GroupBox1Resize(Sender: TObject);
553520begin
554521 ListBox1.Width := GroupBox1.ClientWidth div 3;
555522end;
556523```
557524
558525### Общая ошибка: неправильное событие OnResize
559526
560527Не помещайте *весь* свой код изменения размера \[элементов управления\] в событие Form OnResize. Событие Form OnResize вызывается только при изменении размера формы. Дочерние элементы управления (напр., GroupBox1) изменятся позже, поэтому GroupBox1.ClientWidth все еще \[будет\] иметь старое значение во время события FormResize. Вы должны использовать событие GroupBox1.OnResize, чтобы реагировать на изменения GroupBox1.ClientWidth.
561528
562529### Общая ошибка: Width вместо ClientWidth, AdjustClientRect
563530
564531\[Свойство\] Width - это размер \[элемента управления\], включая рамку. \[Свойство\] ClientWidth - это внутренняя ширина \[элемента управления\] без рамки. Некоторые элементы управления, такие как TPanel, рисуют еще одну рамку. Потом вы используете AdjustClientRect. Тот же пример, но вместо GroupBox1 на Panel1 находится ListBox1:
565532
566533``` pascal
567534procedure TForm1.Panel1Resize(Sender: TObject);
568535var
569536 r: TRect;
570537begin
571538 r := Panel1.ClientRect;
572539 Panel1.AdjustClientRect(r);
573540 ListBox1.Width := (r.Right - r.Left) div 3;
574541end;
575542```
576543
577-\
578-\
579-
580544# Пользовательские элементы управления
581545
582546Когда вы пишете свой собственный элемент управления, вы можете переопределить и тонко настроить многие части автомасштаба LCL.
583547
584548## SetBounds, ChangeBounds, DoSetBounds
585549
586550**SetBounds** вызывается, когда устанавливаются свойства Left, Top, Width, Height, BoundsRect, или пользователь вызывает его напрямую. SetBounds обновляет BaseBounds и BaseParentClientSize, которые используются \[механизмом\] привязки для сохранения размеров. Например, загрузка формы с TMemo и lfm\[-файлом\], содержащим \[значения параметров\] Left и Width \[компонента\] TMemo, вызывает \[метод\] SetBounds для TMemo два раза. Когда пользователь распахивает окно, SetBounds вызывается для формы, но не для Memo, сохраняя BaseBounds Memo. Если Memo привязан справа, \[значение параметра\] Width \[компонента\] Memo изменяется на основе \[кода методов\] BaseBounds и BaseParentClientSize. Имейте в виду, что данные aLeft, aTop, aWidth, aHeight могут быть недействительными и будут изменены LCL перед применением. Delphi чаще всего называет SetBounds. SetBounds вызывает ChangeBounds с KeepBase=false.
587551
588552**ChangeBounds** вызывается всякий раз, когда позиция или размер элемента управления задаются либо с помощью свойств, либо с помощью \[механизма разметки\] LCL. SetBounds вызывает внутри \[себя\] ChangeBounds с KeepBase=false, в то время как \[механизма разметки\] LCL вызывает его с KeepBase=true. Переопределение этого в коде может изменить предпочтительный размер или изменить размеры других элементов управления. Имейте в виду, что данные aLeft, aTop, aWidth, aHeight могут быть недействительными и будут изменены LCL перед применением. Вы можете вызвать эту функцию.
589553
590554**DoSetBounds** - это функция низкого уровня для установки private-переменных FLeft, FTop, FWidth, FHeight. Не вызывайте эту функцию, \[потому что\] только LCL вызывает ее. Она также обновляет FClientWidth и FClientHeight соответственно. Переопределите ее, чтобы обновить содержимое разметки элемента управления, например полосы прокрутки. Как всегда: не рисуйте здесь, но вызывайте Invalidate и рисуйте в OnPaint или переопределите Paint.
591555
592556**DoAdjustClientRectChange** вызывается LCL и интерфейсом LCL, когда ClientRect изменился, а Width и Height сохранились.
593557
594558**WMSize** существует для совместимости с Delphi/VCL. Он вызывается интерфейсом LCL при каждом изменении границ.
595559
596560## AdjustClientRect
597561
598562Метод AdjustClientRect может быть переопределен вашими пользовательскими элементами управления и влияет на \[свойства\] Align, ChildSizing.Layout и AnchorSides. Это не влияет на значение Left, Top и не влияет на нормальный \[механизм\] привязки (например, установку).
599563
600564Когда вы хотите нарисовать свою собственную рамку, тогда дочерние элементы управления должны быть выровнены в этой рамке. Например, TPanel рисует рамку и уменьшает клиентскую область, переопределяя метод **AdjustClientRect**:
601565
602566``` pascal
603567 TCustomPanel = class(TCustomControl)
604568 ...
605569 protected
606570 ...
607571 procedure AdjustClientRect(var aRect: TRect); override;
608572 ...
609573
610574procedure TCustomPanel.AdjustClientRect(var aRect: TRect);
611575var
612576 BevelSize: Integer;
613577begin
614578 inherited AdjustClientRect(aRect);
615579
616580 BevelSize := BorderWidth;
617581 if (BevelOuter <> bvNone) then
618582 inc(BevelSize, BevelWidth);
619583 if (BevelInner <> bvNone) then
620584 inc(BevelSize, BevelWidth);
621585
622586 InflateRect(aRect, -BevelSize, -BevelSize);
623587end;
624588```
625589
626590### AdjustClientRect и выравнивание
627591
628592AdjustClientRect может использоваться для уменьшения клиентской области, используемой всеми операциями \[автоматической установки\] размера. Например, TPanel использует AdjustClientRect для уменьшения клиентской области посредством \[изменения величины\] зазора:
629593
630-<figure>
631-<img src="Adjustclientrect_align.png" title="Adjustclientrect_align.png" />
632-<figcaption>Adjustclientrect_align.png</figcaption>
633-</figure>
594+![](/wiki-images/c/ca/Adjustclientrect_align.png)
634595
635596\[Кнопка\] Button1 на скриншоте была создана со \[значением\] Align=alClient. Также \[был\] задействован \[свойство панели\] ChildSizing.Layout.
636597
637598Когда \[дочерний элемент\] привязывается к родительскому элементу через \[свойство\] AnchorSides, также используется параметр AdjustClientRect:
638599
639600``` pascal
640601 Button1.AnchorParallel(akTop,0,Button1.Parent);
641602```
642603
643604Верхний край Button1 привязан к верхнему краю клиентской области родителя. Если AdjustClientRect добавляет 3px к \[значению\] Top, Button1.Top будет 3px (3 плюс 0).
644605
645606## Собственный AutoSize
646607
647608Когда параметру *AutoSize* установлено значение true, элемент управления должен быть изменен до оптимального размера, если это возможно.
648609
649610### Предпочтительный размер
650611
651612Новый размер выбирается \[механизмом\] LCL через \[метод\] **GetPreferredSize**, который вызывает **CalculatePreferredSize** \[и\] который можно переопределить. Например, давайте опишем **TQuadrat**, который является \[наследником\] TShape, но его высота должна быть равна его ширине:
652613
653614``` pascal
654615 TQuadrat = class(TShape)
655616 protected
656617 procedure CalculatePreferredSize(var PreferredWidth,
657618 PreferredHeight: integer; WithThemeSpace: Boolean); override;
658619 end;
659620...
660621procedure TQuadrat.CalculatePreferredSize(var PreferredWidth,
661622 PreferredHeight: integer; WithThemeSpace: Boolean);
662623begin
663624 PreferredHeight:=Width;
664625end;
665626```
666627
667628Метод CalculatePreferredSize получает два переменных параметра: *PreferredWidth* и *PreferredHeight*. Они по умолчанию равны 0, что означает: нет оптимального размера, поэтому LCL не изменяет размер. Вышеупомянутая функция устанавливает \[свойство\] *PreferredHeight* в текущее \[свойство\] *Width*. Логический параметр *WithThemeSpace* устарел и всегда \[равен\] false.
668629
669630**Важно**: \[метод\] CalculatePreferredSize не должен изменять границы или любое другое значение элемента управления, которое может инициировать \[механизм\] autosize. Это создаст \[бесконечный\] цикл.
670631
671632Вычисление *PreferredWidth/Height* может быть дорогостоящей \[операцией\]. Поэтому LCL кэширует результат до тех пор, пока для элемента управления не будет вызвана \[процедура\] *InvalidatePreferredSize*. В нашем примере *PreferredHeight* зависит от \[параметра\] *Width*, поэтому мы должны \[вызвать\] перерисовку \[элемента управления\], когда изменяется \[параметр\] *Width*:
672633
673634``` pascal
674635 TQuadrat = class(TShape)
675636 protected
676637 ...
677638 procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;
678639 end;
679640...
680641procedure TQuadrat.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);
681642begin
682643 inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);
683644 InvalidatePreferredSize;
684645 // Примечание: здесь [вызов метода] AdjustSize может быть опущен, поскольку LCL делает это после вызова DoSetBounds.
685646end;
686647```
687648
688649LCL автоматически активирует \[механизм\] autosizing'а, когда границы изменяются, поэтому пример является полным.
689650
690651По умолчанию TWinControl для реализации CalculatePreferredSize запрашивает виджетсет, который может вернуть оптимальную ширину и/или высоту. Каждый элемент управления может переопределять метод CalculatePreferredSize. Например, TImage переопределяет его и возвращает размер изображения. Если нет оптимальной ширины (высоты), возвращаемое значение \[будет\] равно 0, и LCL будет сохранять текущую \[величину\] Width (Height). Если 0 - допустимый размер для вашего контроля, вы должны присвоить флагу ControlStyle \[значение\] csAutoSize0x0 (ControlStyle:=ControlStyle+\[csAutoSize0x0\];). Примером может служить\элемент управления LCL TPanel.
691652
692653### AdjustSize
693654
694655Когда предпочтительный размер зависит от нового свойства, то каждый раз, когда свойство изменяется, должен быть вызван \[механизм\] автоматического изменения размера. Например:
695656
696657``` pascal
697658procedure TQuadrat.SetSubTitle(const AValue: string);
698659begin
699660 if FSubTitle = AValue then exit;
700661 FSubTitle := AValue;
701662 InvalidatePreferredSize;
702663 AdjustSize;
703664end;
704665```
705666
706-\
707-\
708-
709667# Сокращение накладных расходов с помощью \[методов\] DisableAutoSizing, EnableAutoSizing
710668
711669Начиная с \[версии\] Lazarus 0.9.29, существует новый алгоритм автоизменения размера, который уменьшает накладные расходы и допускает глубокие вложенные зависимости. До \[версии\] 0.9.28 \[методы\] DisableAlign/EnableAlign и Disable/EnableAutoSize работают только для одного элемента управления и его прямых дочерних контролов.
712670
713671Каждый раз, когда вы меняете \[какое-нибудь\] свойство, LCL запускает \[механизм\] пересчета разметки:
714672
715673``` pascal
716674Label1.Caption := 'A'; // первый перерасчет
717675Label2.Caption := 'B'; // второй перерасчет
718676```
719677
720678Перерасчет будет возбуждать \[механизм вызова\] событий и отправки сообщений. Чтобы уменьшить накладные расходы, вы можете приостановить \[механизм\] автоизменения размера:
721679
722680``` pascal
723681DisableAutoSizing;
724682try
725683 Label1.Caption := 'A'; // нет перерасчета
726684 Label2.Caption := 'B'; // нет перерасчета
727685finally
728686 EnableAutoSizing; // однократный перерасчет
729687end;
730688```
731689
732690Вы должны уравновешивать вызовы Disable/EnableAutoSizing. Автоизменение размера начинается только тогда, когда вызывается EnableAutoSizing после соответствующего (ранее) \[вызова\] DisableAutoSizing.
733691
734692Начиная с \[версии Lazarus\] 0.9.29, Disable/EnableAutoSize работает для всей формы. Это означает, что каждый вызов DisableAutoSizing приостанавливает автоизменение размера для **всех** элементов управления на этой форме. Если вы пишете свой собственный элемент управления, вы можете использовать следующее:
735693
736694``` pascal
737695procedure TMyRadioGroup.DoSomething;
738696begin
739697 DisableAutoSizing; // отключено автоизменение размера не только [для] TRadioGroup, но и [для] всей формы
740698 try
741699 // удаляем элементы ...
742700 // добавляем, тасуем элементы ...
743701 // меняем заголовки элементов ...
744702 finally
745703 EnableAutoSizing; // перерасчет
746704 end;
747705end;
748706```
749707
750708К чему все это: вам не нужно \[ни о чем\] заботиться. Просто вызовите Disable/EnableAutoSizing.
751709
752710> **Note:** вот так неправильно:\
753711> \
754712>
755713> ``` pascal
756714> Button1.DisableAutoSizing;
757715> Label1.EnableAutoSizing; // неверно: каждый элемент управления имеет свой собственный
758716> //счетчик ссылок на автоизменение размера
759717> ```
760718
761719## DisableAutoSizing и границы формы
762720
763721DisableAutoSizing имеет еще один полезный эффект при асинхронных менеджерах окон, которые вы \[можете\] встретить в системах Linux. Каждый раз, когда форма изменяет размер или перемещается, \[ее\] границы отправляются в widgetset (DoSendBoundsToInterface). Даже если форма не отображается, дескриптор изменяется. Менеджер окон зачастую рассматривает эти границы только в качестве предложения. У диспетчера окон есть своя логика, и часто используются только границы, переданные первыми. Второе, третье или дальнейшие перемещения могут быть проигнорированы. С помощью параметра DisableAutoSizing вы можете убедиться, что только последние границы отправляются в виджетсет, что делает границы формы более надежными \[для отображения\].
764722
765723------------------------------------------------------------------------
766724
767725Прим.перев.: очевидно, имеется ввиду прерывистый механизм перерисовки границ окна, когда при перетаскивании его края мышью рисуется только рамка новой границы окна, а окончательная перерисовка происходит после отпускания кнопки мыши.
768726
769727------------------------------------------------------------------------
770728
771-\
772-\
773-
774729# Пример: панель кнопок
775730
776731В этом примере сочетаются несколько механизмов компоновки LCL для создания панели с тремя кнопками: кнопка *Help* слева и кнопки *Ok* и *Cancel* справа. Мы хотим, чтобы панель была внизу формы, заполняя всю \[ее\] ширину. Кнопки и панель автоматически изменяют размеры, чтобы соответствовать всем шрифтам и темам.
777732
778733Шаг 1: Создайте панель и установите ее свойство Align в alBottom. Добавьте три TBitBtns.
779734
780-<figure>
781-<img src="Autosize_example_buttonpanel1.png" title="Autosize_example_buttonpanel1.png" />
782-<figcaption>Autosize_example_buttonpanel1.png</figcaption>
783-</figure>
735+![](/wiki-images/7/7e/Autosize_example_buttonpanel1.png)
784736
785737Установите в свойстве *Kind* BitBtns \[соответственные\] отображения глифов. Возможно, вам нужно будет установить свойство GlyphShowMode в gsmAlways\[("отображать всегда")\], чтобы увидеть их на своей платформе. Установите для свойства *AutoSize* \[кнопок\] BitBtn значение *True*, которое уменьшит/увеличит \[ширину\] кнопок для идеального заполнения \[их\] глифами и текстом. В зависимости от вашей темы и платформы вы можете заметить, что кнопки имеют разную высоту.
786738
787-<figure>
788-<img src="Autosize_example_buttonpanel2.png" title="Autosize_example_buttonpanel2.png" />
789-<figcaption>Autosize_example_buttonpanel2.png</figcaption>
790-</figure>
739+![](/wiki-images/4/49/Autosize_example_buttonpanel2.png)
791740
792741Установите свойство *Align ' кнопки справки в*alLeft*и установите для остальных двух кнопок свойство*Align*в*alRight''. Это увеличит кнопки по вертикали и переместит их в крайнее левое/правое. alLeft/alRight не влияют на ширину, поэтому кнопки используют свою предпочтительную ширину.
793742
794-<figure>
795-<img src="Autosize_example_buttonpanel3.png" title="Autosize_example_buttonpanel3.png" />
796-<figcaption>Autosize_example_buttonpanel3.png</figcaption>
797-</figure>
743+![](/wiki-images/b/b6/Autosize_example_buttonpanel3.png)
798744
799745Высота панели по-прежнему фиксирована. Теперь установите для панели свойство *AutoSize* в *True*. Панель теперь сжимается вертикально, чтобы соответствовать самой высокой кнопке.
800746
801-<figure>
802-<img src="Autosize_example_buttonpanel4.png" title="Autosize_example_buttonpanel4.png" />
803-<figcaption>Autosize_example_buttonpanel4.png</figcaption>
804-</figure>
747+![](/wiki-images/3/36/Autosize_example_buttonpanel4.png)
805748
806749Кнопка *Ok* имеет короткий заголовок, поэтому кнопка очень маленькая. Установите для кнопки *Constraints.MinWidth* в значение *75*. Теперь кнопка несколько расширится.
807750
808-<figure>
809-<img src="Autosize_example_buttonpanel5.png" title="Autosize_example_buttonpanel5.png" />
810-<figcaption>Autosize_example_buttonpanel5.png</figcaption>
811-</figure>
751+![](/wiki-images/3/32/Autosize_example_buttonpanel5.png)
812752
813753Теперь добавьте некоторое пространство вокруг кнопок. Установите для панели *ChildSizing.LeftTopSpacing/RightBottomSpacing/HorizontalSpacing* в значение *6*.
814754
815-<figure>
816-<img src="Autosize_example_buttonpanel6.png" title="Autosize_example_buttonpanel6.png" />
817-<figcaption>Autosize_example_buttonpanel6.png</figcaption>
818-</figure>
755+![](/wiki-images/4/46/Autosize_example_buttonpanel6.png)
819756
820757Наконец, очистите *Caption* панели и установите ее 'BevelOuter*в*bvNone''.
821758
822759![](/wiki-images/4/4e/Autosize_example_buttonpanel7.png)\
823-\
824760
825761# Прокрутка
826762
827763Некоторые элементы управления LCL, такие как *TScrollBox*, *TForm' и*TFrame'', показывают полосы прокрутки, если дочерние элементы управления слишком велики, чтобы поместиться на scrollbox'е, форме или фрейме. Они наследуют это поведение от своего предка **TScrollingWinControl**.
828764
829765Прокручиваемая **логическая клиентская область** элемента управления может быть больше, чем **видимая клиентская область**. **Видимая клиентская область** - это **ClientRect**. Она всегда начинается с \[координат\] 0,0 и его ширина и высота - это внутренняя область. Например, в TGroupBox - это размер области внутри фрейма. Итак, всегда верно следующее:
830766
831767``` pascal
832768ClientWidth <= Width
833769ClientHeight <= Height
834770```
835771
836772**Логическая клиентская область** определяется методом **GetLogicalClientRect**. По умолчанию она совпадает с *ClientRect*. Когда дочерний элемент управления привязан к правой стороне, он использует *логическую клиентская область*. *TScrollingWinControl* переопределяет этот метод и возвращает *Range*\[(диапазон)\] полос прокрутки, если \[логическая клиентская область\] больше, чем *ClientRect*. **Range** можно установить вручную или автоматически с помощью *AutoScroll=true*. Пример для *AutoScroll=true*:
837773
838-<figure>
839-<img src="Autoscroll_all_fit1.png" title="Autoscroll_all_fit1.png" />
840-<figcaption>Autoscroll_all_fit1.png</figcaption>
841-</figure>
774+![](/wiki-images/d/db/Autoscroll_all_fit1.png)
842775
843776Верхняя кнопка имеет фиксированную ширину 200. Нижняя кнопка привязана к правой стороне панели. Поэтому дочерние элементы управления имеют предпочтительную ширину 200. Поскольку панель больше 200, логическая область клиента больше и нижняя кнопка расширяется.
844777
845778Теперь панель сжата, так что ClientWidth становится меньше 200:
846779
847-<figure>
848-<img src="Autoscroll_not_fit1.png" title="Autoscroll_not_fit1.png" />
849-<figcaption>Autoscroll_not_fit1.png</figcaption>
850-</figure>
780+![](/wiki-images/2/27/Autoscroll_not_fit1.png)
851781
852782Предпочтительная ширина по-прежнему равна 200, поэтому логического клиентская область теперь равна 200 и больше, чем видимая клиентская область. Нижняя кнопка имеет ширину 200, а на панели отображается горизонтальная полоса прокрутки.
853783
854784## Позиция прокрутки
855785
856786Изменение положения полосы прокрутки не изменяет \[значения\] Left или Top любого дочернего элемента управления и не изменяет логическую клиентскую область, \[а также\] не влияет на автомасштабирование. Дочерние элементы управления лишь только виртуально перемещаются.
857787
858788## Прокрутка и автомасштаб
859789
860790Когда AutoSize=true, LCL расширяет \[родительский\] элемент управления \[так\], чтобы разместить все его дочерние элементы управления, и никаких полос прокрутки не требуется. Если \[родительский\] элемент управления не может быть расширен, то (только) \[тогда проявляется\] вторичное действие AutoSize: перемещение дочерних элементов управления.
861791
862792# Пристыковка
863793
864794Пристыковка использует описанные методы и свойства этой страницы, см. [[Docking]].
865795
866796# Splitter
867797
868798См. [[TSplitter/ru|TSplitter]].
869799
870800# TLabel.WordWrap
871801
872802**TLabel.WordWrap**\[(перенос слов)\] изменяет поведение предпочтительного размера \[компонента\] label. **WordWrap=true** требует, чтобы \[величина\] Width \[компонента\] label была фиксированной, например, путем привязки левого и правого размера \[компонента\] label. Предпочтительная высота label затем вычисляется путем разбиения \[своства\] Caption на несколько строк.
873803
874804# Автоматическая настройка DPI и автоматическая настройка статической разметки
875805
876806Исторически LCL использовался в основном для создания статической разметки, несмотря на огромное количество опций, которые Lazarus предлагает для гибкой разметки макетов, таких как Align, Anchors и т.д., которые описаны в остальной части этой статьи. Кроме того, он также исторически игнорировал DPI целевого \[рабочего стола\] и вместо этого использовал значения в пикселях для измерения свойств слева, сверху, ширины и высоты элементов управления. Для настольных платформ и Windows CE это работает нормально, но с появлением поддержки LCL для Android это больше нельзя игнорировать. Начиная с Lazarus 0.9.31, LCL может пересчитывать статическую разметку LCL в пикселях \[на разметку\] в виде гибкой сетки. Существует выбор нескольких режимов, \[которые\] позволяют пересчитывать значения пикселей как в абсолютных \[величинах\], так и в корректируемых для DPI, или как считающихся просто частью размера формы.
877807
878808Важно отметить, что эти новые правила влияют только на элементы управления, которые расположены без выравнивания и только с большинством стандартных привязок.
879809
880810В случае, когда значения будут корректироваться для DPI, появляется новое свойство: TCustomForm.DesignTimeDPI, которое должно хранить значение DPI системы, в которой была создана форма. Значения позиционирования будут расширены, когда DPI целевого \[рабочего стола\] будет больше, чем время DPI времени разработки или уменьшено в противном случае. Обычное значение DPI для рабочего стола - 96, и это значение, заданное по умолчанию.
881811
882812`property DesignTimeDPI: Integer read FDesignTimeDPI write FDesignTimeDPI;`
883813
884814Способ настройки разметки можно контролировать с помощью свойства TApplication.LayoutAdjustmentPolicy
885815
886816``` pascal
887817 TLayoutAdjustmentPolicy = (
888818 lapDefault, // Зависимость от виджетсета
889819 lapFixedLayout, // Фиксированная абсолютная разметка на всех платформах
890820 lapAutoAdjustWithoutHorizontalScrolling, // Это используется платформой для смартфонов,
891821 // ось x растягивается, чтобы заполнить экран и
892822 // y масштабируется, чтобы соответствовать DPI
893823 lapAutoAdjustForDPI // Для рабочих столов с использованием High DPI, x и y масштабируются соответственно DPI
894824 );
895825```
896826
897827Следующие новые методы в TControl позволяют \[задать\] принудительную автонастройку разметки в конкретном контроле и во всех его дочерних элементах, или контролировать, как реагируют на это отдельные потомки TControl:
898828
899829``` pascal
900830TControl = class
901831public
902832...
903833 procedure AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;
904834 const AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth: Integer); virtual;
905835 function ShouldAutoAdjustLeftAndTop: Boolean; virtual;
906836 function ShouldAutoAdjustWidthAndHeight: Boolean; virtual;
907837```
908838
909839LCL-CustomDrawn-Android будет вызывать AutoAdjustLayout, например, когда экран вращается.\
910-\
911840
912841# Подробнее
913842
914843Многие элементы управления переопределяют *TControl.DoAutoSize* для выполнения фактического автомасштабирования.
915844
916845ВАЖНО: Многие элементы управления Delphi переопределяют этот метод, и многие вызывают этот метод напрямую после установки некоторых свойств.
917846
918847Во время создания дескриптора не все интерфейсы могут создавать полные Device Contexts\[(контексты устройств)\], которые необходимы для вычисления таких вещей, как размер текста.
919848
920849Вот почему вы всегда должны называть *AdjustSize* вместо `DoAutoSize`.
921850
922851`TControl.AdjustSize` вызывает `DoAutoSize` умным способом.
923852
924853Во время загрузки и создания дескриптора вызовы задерживаются.
925854
926855Этот метод изначально делает то же самое, что и *TWinControl.DoAutoSize*. Но поскольку `DoAutoSize` обычно переопределяется компонентами-потомками, нецелесообразно выполнять все проверки, которые могут привести к слишком большим издержкам. Чтобы уменьшить это, LCL вызывает `AdjustSize`.
927856
928857При установке AutoSize=true LCL автомасштабирует элемент управления по ширине и высоте. Это одна из самых сложных частей LCL, потому что результат зависит от почти сотен свойств. Начнем с простого:
929858
930859LCL будет только автомасштабировать *Width* (Height), если \[это свойство\] свободно для изменения величины. Другими словами - ширина не автомасштабируется, если:
931860
932861- левая и правая стороны привязаны. Вы можете привязать стороны с \[помощью\] свойства **Anchors** или установить свойство *Align* в alTop, alBottom или alClient.
933862- \[параметр\] *Width* ограничен \[значением\] свойства *Constraints*. \[Свойство\] Constraints также может быть переопределено виджетсетом. Например, winapi не позволяет изменять размер выпадающего списка. Или gtk widgetset не позволяет изменять ширину вертикальной полосы прокрутки.
934863
935864То же \[справедливо и для свойства\] *Height*.
936865
937866Новый размер рассчитывается protected-методом **TControl.CalculatePreferredSize**. Этот метод запрашивает виджетсет для \[получения\] подходящих Width и Height. Например, TButton имеет предпочтительные ширину и высоту. TComboBox имеет только предпочтительную высоту. \[У него\] предпочтительная ширина возвращается как 0, и поэтому LCL не автомасштабирует ширину - \[LCL\] сохраняет ширину неизменной. Наконец, у TMemo нет предпочтительной ширины или высоты. Поэтому \[параметр\] AutoSize не влияет на TMemo.
938867
939868Некоторые элементы управления перекрывают этот метод. Например, потомки TGraphicControl, такие как TLabel, не имеют дескриптора окна и поэтому не могут запрашивать виджетсет. Они должны сами рассчитать их предпочтительную ширину и высоту.
940869
941870Виджетсеты должны переопределять метод GetPreferredSize для каждого класса виджетов, который имеет предпочтительный размер (ширина или высота, или оба).
942871
943872## Parent.AutoSize
944873
945874Выше описанное \[дает\] простое объяснение. Реальный алгоритм \[функционально\] предусматривает гораздо больше возможностей и, следовательно, гораздо более сложен.
946875
947876## Свойства / Методы
948877
949878- `Left`
950879- `Top`
951880
952881Если Parent\<\>nil, то \[значения\] Left, Top - это расстояние в пикселях до верхнего левого пикселя клиентской области родителя (не прокручивается). Помните, что клиентская область - это всегда \[область\] *без* рамки и полосы прокрутки родителя. Для пользователей Delphi: некоторые элементы управления VCL, такие как TGroupbox, определяют клиентскую область как весь элемент управления, включая рамку, а некоторые нет - LCL же более последователен, поэтому Delphi \[с ним\] несовместим. Left и Top могут иметь отрицательные \[значения\] или \[быть\] больше, чем область клиента. Некоторые виджетсеты определяют минимум/максимум где-то около 10000\[px\] или более.
953882
954883Когда клиентская область прокручивается, Left и Top остаются неизменными.
955884
956885Во время изменения размера/перемещения \[элемента управления\] Left и Top не всегда синхронизируются с координатами дескриптора объекта.
957886
958887Если Parent=nil, тогда \[значения\] Left, Top зависят от виджетсета и диспетчера окон. До \[версии\] Lazarus 0.9.25 это обычно были координаты экрана верхне-левой части клиентской области формы. Это несовместимо с Delphi. Планируется изменить это \[поведение\] на Left, Top окна.
959888
960889Подсказка: Каждый раз, когда вы изменяете Left и Top, LCL мгновенно приходит в движение и перекомпонует весь макет. Если вы хотите изменить Left *и* Top, используйте взамен:
961890
962891``` pascal
963892with Button1 do
964893 SetBounds(NewLeft, NewTop, Width, Height);
965894```
966895
967-\
968-
969896- `Width`
970897- `Height`
971898
972899Размер в пикселях не должен быть отрицательным, и большинство виджетсетов не допускают Width=0 и/или Height=0. Некоторые элементы управления на некоторых платформах определяют наибольшее минимальное ограничение в Constraints.MinInterfaceWidth/Height. Вместо того, чтобы изменять размер элемента управления в Width=0 и/или Height=0, установите Visible=false или Parent=nil. Во время изменения размера/перемещения \[свойства\] Width и Height не всегда синхронизируются с размером дескриптора объекта.\
973900
974901- `BoundsRect`
975902
976903Аналогично `Bounds(Left, Top, Width, Height)`.
977904
978905Обычная ошибка новичка:
979906
980907``` pascal
981908BoundsRect.Left := 3; // НЕПРАВИЛЬНО: обычная ошибка новичка
982909```
983910
984-\
985911Это не \[оказывает никакого\] влияния, потому что чтение BoundsRect является функцией. Она создает временный TRect в стеке. Вышеупомянутое те же, что и
986912
987913``` pascal
988914var
989915 r: TRect;
990916begin
991917 r := BoundsRect; // получаем границы
992918 r.Left := 3; // изменяем значение в стеке
993919end; // нет изменений
994920```
995921
996-\
997-
998922- `ClientRect`
999923
1000924Left и Top всегда равны 0,0. \[Свойства\] Width и Height - это видимый размер клиентской области в пикселях. Помните, что клиентская область - это \[то, что\] без рамки и без полос прокрутки. В прокручиваемой клиентской области логическая клиентская область может быть больше видимой.\
1001925
1002926- `ClientOrigin`
1003927
1004928Возвращает экранную позицию верхне-левой координаты 0,0 клиентской области. Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в нужную позицию и отправляет сообщение \[о\] перемещении в интерфейс. Интерфейс обрабатывает \[сообщения о\] перемещениях сразу или по очереди.\
1005929
1006930- `LCLIntf.GetClientBounds`
1007931
1008932Возвращает клиентские границы элемента управления. Аналогичен ClientRect, но Left и Top - это расстояния в пикселах до левого и верхнего краев элемента управления. Например, в TGroupBox \[параметры\] Left, Top являются шириной и высотой левой и верхней границ рамки. Прокрутка не влияет на GetClientBounds.\
1009933
1010934- `LCLIntf.GetWindowRect`
1011935
1012936После вызова \[этой функции\] ARect будет областью элемента управления в координатах экрана. Это означает, что Left и Top будут экранной координатой верхне-левого пикселя Дескриптора объекта, а \[значения\] Right и Bottom будут экранной координатой нижне-правого пиксела.\
1013937
1014938- `FBaseBoundsLock: integer`
1015939
1016940Увеличивается/уменьшается \[методами\] LockBaseBounds/UnlockBaseBounds. Используется для сохранения \[поля\] FBaseBounds во время вызова SetBounds.\
1017941
1018942- `FBaseParentClientSize: TPoint`
1019943
1020944Размер Parent.ClientRect действителен для FBaseBounds. \[Поля\] FBaseBounds и FBaseParentClientSize используются для вычисления расстояния для \[параметра выравнивания\] akRight (aBBottom). Когда размер родителя изменяется, LCL знает, какое расстояние сохранить.\
1021945
1022946- `FBoundsRectForNewParent: TRect`
1023947
1024948При смене родителя элемента управления Дескриптор \[объекта\] создается \[заново\], и многое может случиться. Особенно этот процесс ненадежен для пристыкованных форм. Поэтому BoundsRect сохраняется. VCL использует аналогичные механизм.\
1025949
1026950- `fLastAlignedBounds: TRect`
1027951
1028952Для получения пояснения см. `TControl.SetAlignedBounds`. Коротко: он останавливает некоторые циклы между интерфейсом и автомасштабированием в LCL.\
1029953
1030954- `FLastChangebounds: TRect`
1031955
1032956Используется для остановки вызова ChangeBounds с одинаковыми координатами. Это случается очень часто.\
1033957
1034958- `FLastDoChangeBounds: TRect`
1035959
1036960Используется, чтобы избежать вызова OnChangeBounds с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.\
1037961
1038962- `FLastResizeClientHeight: integer`
1039963- `FLastResizeClientWidth: integer`
1040964- `FLastResizeHeight: integer`
1041965- `FLastResizeWidth: integer`
1042966
1043967Используется, чтобы избежать вызова OnResize с теми же координатами. Это подавляет пользовательскую настройку автомасштабирования.\
1044968
1045969- `FLoadedClientSize: TPoint`
1046970
1047971Во время загрузки многие вещи задерживаются, а многие вещи устанавливаются в неправильном порядке и ухудшаются. Вот почему сохраняется и вновь вызывается *SetClientWidth/SetClientHeight* в конце загрузки. Таким образом, LCL может восстанавливать размеры (напр., при *akRight*), используемые во время проектирование.\
1048972
1049973- `FReadBounds: TRect`
1050974
1051975Аналогично `FLoadedClientSiz`, но для `SetLeft`, `SetTop`, `SetWidth`, `SetHeight`.\
1052976
1053977- `procedure SetBoundsRectForNewParent(const AValue: TRect);`
1054978
1055979Используется для установки `FBoundsRectForNewParent`. См. выше.\
1056980
1057981- `procedure SetAlignedBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;`
1058982
1059983Как SetBounds, но без изменения размеров по умолчанию.\
1060984
1061-- <tt>procedure SetInitialBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;\<//tt\>
985+- procedure SetInitialBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;\<//tt\>
1062986
1063987"Умная" версия SetBounds, уменьшающая накладные расходы при создании и загрузке.\
1064988
1065989- `procedure UpdateBaseBounds(StoreBounds, StoreParentClientSize, UseLoadedValues: boolean); virtual;`
1066990
1067991Фиксирует текущие границы базовых границ.\
1068992
1069993- `procedure SetClientHeight(Value: Integer);`
1070994- `procedure SetClientSize(Value: TPoint);`
1071995- `procedure SetClientWidth(Value: Integer);`
1072996
1073997Существует также для совместимости с Delphi. Изменяет размер элемента управления, чтобы получить желаемый размер ClientRect.\
1074998
1075999- `procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;`
10761000
10771001Это внутренний SetBounds. Применяет ограничения, обновляет базовые границы, вызывает OnChangeBound, OnResize, блокирует границы.\
10781002
10791003- `procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;`
10801004
10811005Этот \[метод\] действительно устанавливает частные private-переменные FLeft, FTop, FWidth, FHeight.\
10821006
10831007- `procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;`
10841008
10851009Это стандартная процедура, переопределяющая многие элементы управления Delphi. Также переопределяет TWinControl.
10861010
10871011- - игнорирует вызовы, когда границы заблокированы
10881012 - блокирует FBoundsRealized, чтобы избежать накладных расходов на интерфейс во время автомасштабирования. ChangeBounds таким образом не блокируется.\
10891013
10901014- `Function GetClientOrigin: TPoint; virtual;`
10911015
10921016Координаты экрана Left, Top клиентской области.\
10931017
10941018- `Function GetClientRect: TRect; virtual;`
10951019
10961020Размер клиентской области. (всегда Left=0, Top=0)\
10971021
10981022- `Function GetScrolledClientRect: TRect; virtual;`
10991023
11001024Видимая клиентская область ClientRect.\
11011025
11021026- `function GetChildsRect(Scrolled: boolean): TRect; virtual;`
11031027
11041028Возвращает клиентский прямоугольник относительно Left, Top элемента управления. Если \[свойство\] Scrolled \[имеет значение\] true, прямоугольник перемещается текущими значениями прокрутки (например, см. TScrollingWincontrol).\
11051029
11061030- `function GetClientScrollOffset: TPoint; virtual;`
11071031
11081032Возвращает смещение прокрутки клиентской области.\
11091033
11101034- `function GetControlOrigin: TPoint; virtual;`
11111035
11121036Возвращает экранные координаты верхне-левой координаты 0,0 области элемента управления (верхне-левый пиксель элемента управления на экране). Обратите внимание, что это значение является позицией, сохраненной в интерфейсе, и не всегда синхронизируется с LCL. Когда элемент управления перемещается, LCL устанавливает границы в желаемую позицию и отправляет сообщение о перемещении в интерфейс. \[А уж\] интерфейс обрабатывает перемещаемый дескриптер сразу или в \[порядке\] очереди.\
1113-\
11141037
11151038# ЧаВо
11161039
11171040## Почему \[свойство\] AutoSize не работает в дизайнере должным образом?
11181041
11191042В дизайнере элементы управления можно перемещать, и свойства могут быть установлены практически в любом порядке. Чтобы разрешать это и избегать возможных конфликтов, \[свойство\] AutoSizing не обновляется при каждом изменении во время разработки.
11201043
11211044## Почему TForm.AutoSize не работает, когда что-то меняется?
11221045
11231046См. [[Autosize / Layout/ru|Автомасштаб и формы]]
11241047
11251048## Нужно ли мне вызывать Application.ProcessMessages при создании большого количества элементов управления?
11261049
11271050Application.ProcessMessages вызывается LCL автоматически после каждого сообщения (например, после каждого события, такого как OnClick). Вызов его сам по себе необходим только в том случае, если изменения должны немедленно стать видимыми пользователю. Например:
11281051
11291052``` pascal
11301053procedure TFrom.Button1Click(Sender: TObject);
11311054begin
11321055 // изменяем ширину элемента управления
11331056 Button1.Width := Button1.Width + 10;
11341057 // применяем все необходимые изменения и перерисовываем кнопку
11351058 Application.ProcessMessages;
11361059 // делаем много вещей, которые занимают много времени
11371060 ...
11381061 // после выхода из OnClick LCL автоматически обрабатывает сообщения
11391062end;
11401063```
11411064
11421065## При включении привязки во время исполнения \[приложения\] элементы управления изменяют размеры, но не используют текущие значения. Почему?
11431066
11441067akBottom означает: сохранить расстояние до нижней части родителя. Зазоры для удержания определяются основными границами. Они устанавливаются в режиме разработки или во время исполнения вызовом \[методов\] SetBounds или UpdateBaseBounds.
11451068
11461069Например: TListBox (Anchors=\[akLeft,aTop\]) при проектировании имеет нижний зазор в 100 пикселей. И кнопку для включения/выключения \[значения\] akBottom TListBox'а. Теперь запустите приложение и нажмите кнопку, чтобы включить akBottom. 100-пиксельное расстояние будет активировано, потому что это был последний раз, когда программист определил базовые границы TListBox. Все остальные изменения были сделаны LCL и не имеют значения. Основные границы\[, заданные\] программистом определяют правила. Вы можете изменить размер формы, но 100 пикселей будет сохранено. Чтобы использовать текущие границы в качестве базовых, используйте:
11471070
11481071``` pascal
11491072ListBox1.UpdateBaseBounds(true, true, false);
11501073ListBox1.Anchors := ListBox1.Anchors + [akBottom];
11511074```
11521075
11531076Установка привязки не вызывает автоматически \[вызов метода\] UpdateBaseBounds, потому что это само по себе может уничтожить возможность изменения свойств.
11541077
11551078## Изменение размера столбцов stringgrid в событии OnResize формы не работает
11561079
11571080\[Событие\] OnResize формы запускается, когда изменяются \[значения\] Width, Height, ClientWidth или ClientHeight формы. Это само по себе не зависит \[непосредственно\] от TStringGrid. Конечно, часто бывает, что и форма, и TStringGrid изменяют размеры. Это означает\[, что в таких случаях\] использование \[события\] OnResize формы часто будет работать, но не всегда. Ярким примером \[того, что использование OnResize формы\] всегда терпит неудачу, является \[случай\], когда тема изменена, и у вас есть TStringGrid\[, лежащий\] в TGroupBox на TForm. Когда тема меняется, размеры формы сохраняются, поэтому никакого \[события\] OnResize формы не запускается. Но меняется \[соответственно теме\] TGroupBox, поэтому TStringGrid должен быть изменен.
11581081
11591082Решение: используйте \[событие\] OnResize TStringGrid'а.
11601083
11611084## Почему TForm.Width равен TForm.ClientWidth?
11621085
11631086Примечание Mattias'а:
11641087
11651088"Есть исторические и технические причины.
11661089
11671090Для форм без родителя Clientwidth равен Width, поскольку реальная ширина, включая рамку, не была доступна в Linux десять лет назад (по крайней мере, не достоверно для разных оконных менеджеров). Я не тестировал, но слышал, что теперь это возможно с gtk2. Основная проблема заключается в автомасштабировании, потому что для этого нужны размеры рамки до того, как форма будет отображена на экране. Возможно, \[размер рамки\] доступен только после \[наступления\] события, а это значит, что вам нужно подождать, что означает проблему для ShowModal. Изменив это, вы нарушите совместимость с большим количеством существующего кода [[LCL/ru]], но для этого мы добавили LCLVersion в файлы lfm.
11681091
11691092Существует новое определение компилятора **LCLRealFormBounds** в Lazarus trunk 1.7, которое позволяет использовать реальный размер для формы. Чтобы использовать его, просто скомпилируйте свое приложение с помощью `LCLRealFormBounds ON`. Пока что поддерживается только widgetset win32.
11701093
11711094Для всех остальных элементов управления применяются правила ClientWidth\<=Width. Width - это ClientWidth плюс рамка виджета. Вопрос в том, принадлежат ли полосы прокрутки рамке. Я бы сказал "да", и это было реализовано таким образом некоторое время назад. Видимо, это изменилось. См. Текущую проблему с курсором в synedit."
11721095
11731096## Я получаю бесконечный цикл / Как отладить автомасштабирование?
11741097
11751098Вот некоторые примечания, что другие пользователи сделали неправильно и как найти выход:
11761099
11771100### Отличия от Delphi
11781101
11791102Для пользователей Delphi: пожалуйста, прочитайте: [[Autosize / Layout/ru|Отличия от Delphi]]
11801103
11811104### Переопределение метода [[LCL/ru|LCL]] и запуск пересчета
11821105
11831106Вы переопределяете метод и просите [[LCL/ru|LCL]] пересчитать снова, даже если ничего не изменилось. Проверьте настройки AdjustSize, Realign, AlignControls, InvalidatePreferredSize. Например:
11841107
11851108``` pascal
11861109procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);
11871110begin
11881111 // Это создаст бесконечный цикл
11891112 InvalidatePreferredSize;
11901113
11911114 // Это также создаст бесконечный цикл:
11921115 OtherComponent.Left:=10;
11931116 OtherComponent.Left:=20;
11941117
11951118 inherited SetBounds(ALeft, ATop, AWidth, AHeight);
11961119end;
11971120```
11981121
11991122Объяснение: метод SetBounds вызывается часто, даже ничего не меняя. Например, так будет делать "Left:=30;".
12001123
12011124Решение: отслеживайте изменения:
12021125
12031126``` pascal
12041127procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);
12051128begin
12061129 if (Left <> ALeft) or (Top <> ATop) or (Width <> AWidth) or (Height <> AHeight) then
12071130 InvalidatePreferredSize;
12081131 inherited SetBounds(ALeft, ATop, AWidth, AHeight);
12091132end;
12101133```
12111134
12121135#### TWinControl.AlignControls
12131136
12141137``` pascal
12151138procedure AlignControls(aControl: TControl; var aRect: TRect); override;
12161139```
12171140
12181141AlignControls перемещает и упорядочивает все дочерние элементы управления. Реализация [[LCL/ru|LCL]] игнорирует элементы управления с помощью Align=alCustom.
12191142
12201143Параметр `aControl: TControl` поддерживается для совместимости с VCL. [[LCL/ru|LCL]] всегда возвращает nil. Он дает приоритет aControl при применении свойства Align. Если у вас есть, например, два элемента управления A,B со \[значением\] Align=alLeft, то \[элемент управления, который будет\] нижним слева расположится левее \[другого\]. Если оба имеют одинаковые \[значения\] Left, \[то\] приоритетнее \[элемент в\] порядке создания. Теперь представьте, что вы хотите переключить оба элемента управления A, B в дизайнере. Вы перетащите B влево. Это приведет к установке \[значения\] B.Left в 0. Теперь AlignControls запускается и находит\[, что у обоих элементов управления значения\] A.Left=0 и B.Left=0. В обычном \[случае\] A \[по расположению окажется в\] приоритете. Чтобы \[приоритетнее оказался элемент управления\] B, VCL вызовет AlignControls(B,r). Таким образом, aControl является последним перемещенным. В отличие от VCL, [[LCL/ru|LCL]] позволяет комбинировать несколько изменений разметки без перерасчета на каждом шагу. [[LCL/ru|LCL]] отслеживает последние перемещенные элементы управления в TWinControl.fAlignControls и применяет порядок в TWinControl.CreateControlAlignList. Параметр aControl всегда равен nil.
12211144
12221145Смотри `TWinControl.CreateControlAlignList`.\
12231146
12241147### OnResize/OnChangeBounds конфликтует со свойствами LCL
12251148
12261149Вы устанавливаете границы, которые ущемляют свойства LCL. Например, по умолчанию TLabel.AutoSize \[имеет значение\] true. Если вы определяете Label1.Width в событии OnResize, LCL будет \[запускать\] пересчет, изменяя размер Label1, и снова вызывать OnResize. Запустите приложение в отладчике и воспроизведите ошибку. Когда оно войдет в \[бесконечный\] цикл, приостановите приложение и смотрите стек вызовов. Как вы видите, одно из ваших событий или ваших методов начинают искать там. Например:
12271150
12281151``` pascal
12291152procedure TMainForm.FormResize(Sender: TObject);
12301153begin
12311154 // если Button1 привязан или AutoSize=true, то следующее может создать бесконечный цикл:
12321155 Button1.Width:=100;
12331156end;
12341157```
12351158
12361159### Ошибка интерфейса LCL, пользовательский виджет
12371160
12381161Иногда интерфейс LCL или ваш пользовательский элемент управления имеет ошибку и отменяет границы LCL. Скомпилируйте LCL с \[опцией\] `-dVerboseIntfSizing`. Это запишет связь между LCL и интерфейсом LCL. Если виджет не допускает свободное изменение размера, он должен сказать \[об этом\] LCL через Constraints\[(ограничения)\]. Поищите `SetInterfaceConstraints` в различных интерфейсах LCL и `TWSControlClass.ConstraintWidth/Height`.
12391162
12401163### alCustom
12411164
12421165Вы используете alCustom. В ранних версиях Lazarus это было реализовано только наполовину, но некоторые даровитые программисты использовали его для создания некоторых специальных эффектов. Теперь это реализовано, и ваша программа больше не работает. Пожалуйста, см. здесь, что alCustom делает: [[Autosize / Layout/ru|alCustom]].
12431166
12441167# См. также
12451168
12461169- [[Docking]]
12471170- [[Anchor Sides/ru|Anchor Sides]]
12481171
12491172---
12501173*From the Free Pascal wiki, page "Autosize / Layout/ru", as it stood on 2023-09-05 after a change by Zoltanleo, 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